From dfffa760693bc3dea6e778cc225c3eab0c88c23a Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Wed, 13 Mar 2024 02:35:09 +0100 Subject: [PATCH 001/134] Remove unnecessary getter methods in PlaceholderReplacer Removed the private boolean methods isFailOnUnresolvedExpression() and leaveEmptyOnExpressionError() in the PlaceholderReplacer class. These methods are redundant as the respective boolean variables can be accessed directly. --- .../docxstamper/replace/PlaceholderReplacer.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java b/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java index d6a4cb4b..e1c080c4 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java @@ -120,7 +120,7 @@ public void resolveExpressionsForParagraph( replacement); replace(paragraphWrapper, placeholder, replacementObject); } catch (SpelEvaluationException | SpelParseException e) { - if (isFailOnUnresolvedExpression()) { + if (failOnUnresolvedExpression) { String message = "Expression %s could not be resolved against context of type %s" .formatted(placeholder, expressionContext.getClass()); @@ -132,7 +132,7 @@ public void resolveExpressionsForParagraph( expressionContext.getClass(), e.getMessage()); log.trace("Reason for skipping expression:", e); - if (leaveEmptyOnExpressionError()) { + if (leaveEmptyOnExpressionError) { replace(paragraphWrapper, placeholder, ""); } else if (replaceUnresolvedExpressions()) { replace(paragraphWrapper, @@ -156,14 +156,6 @@ private void replace( replacementRun == null ? RunUtil.create("") : replacementRun); } - private boolean isFailOnUnresolvedExpression() { - return failOnUnresolvedExpression; - } - - private boolean leaveEmptyOnExpressionError() { - return leaveEmptyOnExpressionError; - } - /** * Replaces expressions in the given paragraph and replaces them with the values provided by the expression resolver. * From b25d5af9c1e6493571be0d9d13137be3dffb713e Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Wed, 13 Mar 2024 02:40:30 +0100 Subject: [PATCH 002/134] Refactor PlaceholderReplacer for readability Simplified the PlaceholderReplacer.java class by renaming some variables to improve readability such as `resolverRegistry` to `registry` and `expressionResolver` to `resolver`. Also, removed unnecessary importation of `List` and `ExpressionUtil`. Lastly, employed the usage of the 'var' keyword to enhance code clarity. --- .../replace/PlaceholderReplacer.java | 52 +++++++++---------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java b/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java index e1c080c4..34bc043a 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java @@ -11,15 +11,15 @@ import org.springframework.expression.spel.SpelParseException; import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.el.ExpressionResolver; -import org.wickedsource.docxstamper.el.ExpressionUtil; import org.wickedsource.docxstamper.replace.typeresolver.ObjectResolverRegistry; import org.wickedsource.docxstamper.util.ParagraphWrapper; import org.wickedsource.docxstamper.util.RunUtil; import org.wickedsource.docxstamper.util.walk.BaseCoordinatesWalker; -import java.util.List; import java.util.Optional; +import static org.wickedsource.docxstamper.el.ExpressionUtil.findVariableExpressions; + /** * Replaces expressions in a document with the values provided by the {@link ExpressionResolver}. * @@ -31,8 +31,8 @@ public class PlaceholderReplacer { private static final Logger log = LoggerFactory.getLogger( PlaceholderReplacer.class); - private final ExpressionResolver expressionResolver; - private final ObjectResolverRegistry resolverRegistry; + private final ExpressionResolver resolver; + private final ObjectResolverRegistry registry; private final boolean failOnUnresolvedExpression; private final boolean leaveEmptyOnExpressionError; private final boolean replaceUnresolvedExpressions; @@ -42,8 +42,8 @@ public class PlaceholderReplacer { /** *

Constructor for PlaceholderReplacer.

* - * @param resolverRegistry the registry containing all available type resolvers. - * @param expressionResolver the expression resolver used to resolve expressions in the document. + * @param registry the registry containing all available type resolvers. + * @param resolver the expression resolver used to resolve expressions in the document. * @param failOnUnresolvedExpression if set to true, an exception is thrown when an expression cannot be * resolved. * @param replaceUnresolvedExpressions if set to true, expressions that cannot be resolved are replaced by the @@ -57,16 +57,16 @@ public class PlaceholderReplacer { * replaced with a line break. */ public PlaceholderReplacer( - ObjectResolverRegistry resolverRegistry, - ExpressionResolver expressionResolver, + ObjectResolverRegistry registry, + ExpressionResolver resolver, boolean failOnUnresolvedExpression, boolean replaceUnresolvedExpressions, String unresolvedExpressionsDefaultValue, boolean leaveEmptyOnExpressionError, String lineBreakPlaceholder ) { - this.resolverRegistry = resolverRegistry; - this.expressionResolver = expressionResolver; + this.registry = registry; + this.resolver = resolver; this.failOnUnresolvedExpression = failOnUnresolvedExpression; this.replaceUnresolvedExpressions = replaceUnresolvedExpressions; this.unresolvedExpressionsDefaultValue = unresolvedExpressionsDefaultValue; @@ -98,38 +98,36 @@ protected void onParagraph(P paragraph) { /** * Finds expressions in the given paragraph and replaces them with the values provided by the expression resolver. * - * @param p the paragraph in which to replace expressions. - * @param expressionContext the context root - * @param document the document in which to replace all expressions. + * @param p the paragraph in which to replace expressions. + * @param context the context root + * @param document the document in which to replace all expressions. */ public void resolveExpressionsForParagraph( P p, - Object expressionContext, + Object context, WordprocessingMLPackage document ) { - ParagraphWrapper paragraphWrapper = new ParagraphWrapper(p); - List placeholders = ExpressionUtil.findVariableExpressions( - paragraphWrapper.getText()); - for (String placeholder : placeholders) { + var paragraphWrapper = new ParagraphWrapper(p); + var placeholders = findVariableExpressions(paragraphWrapper.getText()); + for (var placeholder : placeholders) { try { - Object replacement = expressionResolver.resolveExpression( - placeholder, - expressionContext); - R replacementObject = resolverRegistry.resolve(document, - placeholder, - replacement); - replace(paragraphWrapper, placeholder, replacementObject); + var resolution = resolver.resolveExpression(placeholder, + context); + var replacement = registry.resolve(document, + placeholder, + resolution); + replace(paragraphWrapper, placeholder, replacement); } catch (SpelEvaluationException | SpelParseException e) { if (failOnUnresolvedExpression) { String message = "Expression %s could not be resolved against context of type %s" .formatted(placeholder, - expressionContext.getClass()); + context.getClass()); throw new DocxStamperException(message, e); } else { log.warn( "Expression {} could not be resolved against context root of type {}. Reason: {}. Set log level to TRACE to view Stacktrace.", placeholder, - expressionContext.getClass(), + context.getClass(), e.getMessage()); log.trace("Reason for skipping expression:", e); if (leaveEmptyOnExpressionError) { From 8e0733e5d8224fefc3cd04b3cbc9c67fc305fea8 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Wed, 13 Mar 2024 03:53:59 +0100 Subject: [PATCH 003/134] Refactor code to handle expressions over placeholders This commit cleans up the codebase by transitioning to a term 'expression' over 'placeholder'. Furthermore, it includes renaming and reusing of classes, including converting certain String-based expressions into Expression objects, and performing necessary expression cleaning as a part of the newly introduced Expression class. Multiple occurrences of 'placeholder' in comments and functionalities are replaced with 'expression' to align with the changes. Tests and function calls are reframed to pass or handle new Expression objects as necessary. --- .../wickedsource/docxstamper/DocxStamper.java | 6 +- .../docxstamper/DocxStamperConfiguration.java | 5 +- .../api/typeresolver/ITypeResolver.java | 12 +- .../docxstamper/el/ExpressionResolver.java | 31 +- .../docxstamper/el/ExpressionUtil.java | 14 +- .../processor/BaseCommentProcessor.java | 2 +- .../processor/CommentProcessorRegistry.java | 44 +-- .../displayif/DisplayIfProcessor.java | 2 +- .../repeat/ParagraphRepeatProcessor.java | 4 +- .../ParagraphResolverDocumentWalker.java | 7 +- .../repeat/RepeatDocPartProcessor.java | 19 +- .../replace/PlaceholderReplacer.java | 45 +-- .../typeresolver/LegacyFallbackResolver.java | 2 +- .../typeresolver/ObjectResolverRegistry.java | 17 +- .../replace/typeresolver/TypeResolver.java | 18 +- .../docxstamper/util/ParagraphWrapper.java | 343 ++++++++++-------- .../docxstamper/api/ObjectResolver.java | 32 +- .../verron/docxstamper/core/Expression.java | 53 +++ .../verron/docxstamper/core}/Matcher.java | 2 +- .../preset/resolver/ImageResolver.java | 2 +- .../preset/resolver/Null2DefaultResolver.java | 2 +- .../resolver/Null2PlaceholderResolver.java | 17 +- .../preset/resolver/Resolvers.java | 2 +- .../preset/resolver/StringResolver.java | 10 +- .../preset/resolver/ToStringResolver.java | 2 +- .../docxstamper/el/ExpressionUtilTest.java | 69 ---- .../replace/ParagraphWrapperTest.java | 56 --- 27 files changed, 404 insertions(+), 414 deletions(-) create mode 100644 src/main/java/pro/verron/docxstamper/core/Expression.java rename src/main/java/{org/wickedsource/docxstamper/el => pro/verron/docxstamper/core}/Matcher.java (95%) delete mode 100644 src/test/java/org/wickedsource/docxstamper/el/ExpressionUtilTest.java diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java index 6733a98b..8d806512 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java @@ -5,6 +5,7 @@ import org.springframework.expression.TypedValue; import org.springframework.expression.spel.SpelParserConfiguration; import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.lang.NonNull; import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.api.EvaluationContextConfigurer; import org.wickedsource.docxstamper.api.preprocessor.PreProcessor; @@ -16,6 +17,7 @@ import pro.verron.docxstamper.OpcStamper; import pro.verron.docxstamper.StamperFactory; import pro.verron.docxstamper.api.ObjectResolver; +import pro.verron.docxstamper.core.Expression; import java.io.InputStream; import java.io.OutputStream; @@ -74,7 +76,7 @@ private DocxStamper( boolean replaceUnresolvedExpressions, boolean leaveEmptyOnExpressionError, String unresolvedExpressionsDefaultValue, - String lineBreakPlaceholder, + @NonNull String lineBreakPlaceholder, EvaluationContextConfigurer evaluationContextConfigurer, Map, Object> expressionFunctions, List resolvers, @@ -112,7 +114,7 @@ private DocxStamper( replaceUnresolvedExpressions, unresolvedExpressionsDefaultValue, leaveEmptyOnExpressionError, - lineBreakPlaceholder + new Expression(lineBreakPlaceholder) ); for (var entry : configurationCommentProcessors.entrySet()) { diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java index 06b7755d..dd588c39 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java @@ -3,6 +3,7 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.springframework.expression.EvaluationContext; import org.springframework.expression.spel.SpelParserConfiguration; +import org.springframework.lang.NonNull; import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.api.EvaluationContextConfigurer; import org.wickedsource.docxstamper.api.preprocessor.PreProcessor; @@ -39,7 +40,7 @@ public class DocxStamperConfiguration { private final List resolvers = new ArrayList<>(); private final Map, Object> expressionFunctions = new HashMap<>(); private final List preprocessors = new ArrayList<>(); - private String lineBreakPlaceholder; + private String lineBreakPlaceholder = "\n"; private EvaluationContextConfigurer evaluationContextConfigurer = new DefaultEvaluationContextConfigurer(); private boolean failOnUnresolvedExpression = true; private boolean leaveEmptyOnExpressionError = false; @@ -310,7 +311,7 @@ public String getLineBreakPlaceholder() { * @param lineBreakPlaceholder the String that should be replaced with line breaks during stamping. * @return the configuration object for chaining. */ - public DocxStamperConfiguration setLineBreakPlaceholder(String lineBreakPlaceholder) { + public DocxStamperConfiguration setLineBreakPlaceholder(@NonNull String lineBreakPlaceholder) { this.lineBreakPlaceholder = lineBreakPlaceholder; return this; } diff --git a/src/main/java/org/wickedsource/docxstamper/api/typeresolver/ITypeResolver.java b/src/main/java/org/wickedsource/docxstamper/api/typeresolver/ITypeResolver.java index 12ff0f7e..1eebb260 100644 --- a/src/main/java/org/wickedsource/docxstamper/api/typeresolver/ITypeResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/api/typeresolver/ITypeResolver.java @@ -7,7 +7,8 @@ /** *

* A type resolver is responsible for mapping an object of a certain Java class to an object of the DOCX4J api that - * can be put into the .docx document. Type resolvers are used to replace placeholders within the .docx template. + * can be put into the .docx document. Type resolvers are used to replace + * expressions within the .docx template. *

*

* Example: if an expression returns a Date object as result, this date object is passed to a DateResolver which @@ -23,7 +24,6 @@ * @author Tom Hombergs * @version ${version} * @since 1.0.0 - * * @deprecated as of version 1.6.7, replaced by {@link ObjectResolver}. * The new resolver is more versatile, requires less reflection mechanism, * and simplifies the internal workings of the docx-stamper project. @@ -31,13 +31,11 @@ @Deprecated(since = "1.6.7", forRemoval = true) public interface ITypeResolver { /** - * This method is called when a placeholder in the .docx template is to - * be replaced by the result of an expression that - * was found in the .docx template. It creates an object of the DOCX4J api that is put in the place of the found - * expression. + * This method is called when an expression is found in the .docx template. + * It creates an object of the DOCX4J api in the place of the found expression. * * @param document the Word document that can be accessed via the - * DOCX4J api. + * DOCX4J api. * @param expressionResult the result of an expression. Only objects of classes this type resolver is registered for * within the TypeResolverRegistry are passed into this method. * @return an object of the DOCX4J api (usually of type {@link R} = "run diff --git a/src/main/java/org/wickedsource/docxstamper/el/ExpressionResolver.java b/src/main/java/org/wickedsource/docxstamper/el/ExpressionResolver.java index f53b3ef0..69ecb9b1 100644 --- a/src/main/java/org/wickedsource/docxstamper/el/ExpressionResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/el/ExpressionResolver.java @@ -4,6 +4,7 @@ import org.springframework.expression.spel.SpelParserConfiguration; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; +import pro.verron.docxstamper.core.Expression; /** * Resolves expressions against a given context object. Expressions can be either SpEL expressions or simple property @@ -15,8 +16,7 @@ * @since 1.0.0 */ public class ExpressionResolver { - private static final Matcher DEFAULT_MATCHER = new Matcher("${", "}"); - private static final Matcher SECONDARY_MATCHER = new Matcher("#{", "}"); + private final ExpressionParser parser; private final StandardEvaluationContext evaluationContext; @@ -35,30 +35,15 @@ public ExpressionResolver( } /** - * Cleans the given expression by stripping the prefix and suffix if they match any of the configured matchers. - * - * @param expression the expression to clean. - * @return the cleaned expression. - */ - public static String cleanExpression(String expression) { - if (DEFAULT_MATCHER.match(expression)) - return DEFAULT_MATCHER.strip(expression); - if (SECONDARY_MATCHER.match(expression)) - return SECONDARY_MATCHER.strip(expression); - return expression; - } - - /** - * Resolves the given expression against the given context object. + * Resolves the given expression against the provided context object. * - * @param expression the expression to resolve. - * @param contextRoot the context object against which to resolve the expression. - * @return the result of the expression evaluation. + * @param expression the expression to resolve. + * @param contextRoot the context object against which to resolve the expression. + * @return the resolved value of the expression. */ - public Object resolveExpression(String expression, Object contextRoot) { - expression = cleanExpression(expression); + public Object resolve(Expression expression, Object contextRoot) { evaluationContext.setRootObject(contextRoot); - return parser.parseExpression(expression) + return parser.parseExpression(expression.inner()) .getValue(evaluationContext); } } diff --git a/src/main/java/org/wickedsource/docxstamper/el/ExpressionUtil.java b/src/main/java/org/wickedsource/docxstamper/el/ExpressionUtil.java index 030c64a2..6a6da6b7 100644 --- a/src/main/java/org/wickedsource/docxstamper/el/ExpressionUtil.java +++ b/src/main/java/org/wickedsource/docxstamper/el/ExpressionUtil.java @@ -2,6 +2,7 @@ import org.springframework.lang.NonNull; import org.wickedsource.docxstamper.api.DocxStamperException; +import pro.verron.docxstamper.core.Expression; import java.util.ArrayList; import java.util.List; @@ -33,19 +34,22 @@ private ExpressionUtil() { * @param text the text to find expressions in. * @return a list of expressions (including the starting "${" and trailing "}"). */ - public static List findVariableExpressions(@NonNull String text) { + public static List findVariableExpressions(@NonNull String text) { return findExpressions(text, VARIABLE_EXPRESSION_PATTERN); } - private static List findExpressions(@NonNull String text, Pattern pattern) { + private static List findExpressions( + @NonNull String text, + Pattern pattern + ) { if (text.isEmpty()) return emptyList(); Matcher matcher = pattern.matcher(text); int index = 0; - List matches = new ArrayList<>(); + List matches = new ArrayList<>(); while (matcher.find(index)) { String match = matcher.group(); - matches.add(match); + matches.add(new Expression(match)); index = matcher.end(); } return matches; @@ -58,7 +62,7 @@ private static List findExpressions(@NonNull String text, Pattern patter * @param text the text to find expressions in. * @return a list of expressions (including the starting "#{" and trailing "}"). */ - public static List findProcessorExpressions(@NonNull String text) { + public static List findProcessorExpressions(@NonNull String text) { return findExpressions(text, PROCESSOR_EXPRESSION_PATTERN); } diff --git a/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java index 172da751..2ac7b990 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java @@ -21,7 +21,7 @@ public abstract class BaseCommentProcessor implements ICommentProcessor { /** - * PlaceholderReplacer used to replace placeholders in the comment text. + * PlaceholderReplacer used to replace expressions in the comment text. */ protected final PlaceholderReplacer placeholderReplacer; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java index 07180ffc..71bb116d 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java @@ -18,11 +18,17 @@ import org.wickedsource.docxstamper.util.CommentWrapper; import org.wickedsource.docxstamper.util.ParagraphWrapper; import org.wickedsource.docxstamper.util.walk.BaseCoordinatesWalker; +import pro.verron.docxstamper.core.Expression; import java.math.BigInteger; -import java.util.*; +import java.util.ArrayList; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; import static org.wickedsource.docxstamper.el.ExpressionUtil.findProcessorExpressions; +import static org.wickedsource.docxstamper.util.CommentUtil.getCommentString; +import static org.wickedsource.docxstamper.util.CommentUtil.getComments; /** * Allows registration of {@link ICommentProcessor} objects. Each registered @@ -73,10 +79,12 @@ public CommentProcessorRegistry( * @param expressionContext the context root object * @param a T class */ - public void runProcessors(final WordprocessingMLPackage document, final T expressionContext) { - final Map comments = CommentUtil.getComments( - document); - final List proceedComments = new ArrayList<>(); + public void runProcessors( + WordprocessingMLPackage document, + T expressionContext + ) { + var comments = getComments(document); + var proceedComments = new ArrayList(); new BaseCoordinatesWalker() { @Override @@ -154,29 +162,23 @@ private void runProcessorsOnInlineContent( T expressionContext, P paragraph ) { - ParagraphWrapper paragraphWrapper = new ParagraphWrapper(paragraph); - List processorExpressions = findProcessorExpressions( - paragraphWrapper.getText()); - - for (String processorExpression : processorExpressions) { - String strippedExpression = ExpressionResolver.cleanExpression( - processorExpression); + var paragraphWrapper = new ParagraphWrapper(paragraph); + var expressions = findProcessorExpressions(paragraphWrapper.getText()); + for (var expression : expressions) { for (final Object processor : commentProcessors.values()) { ((ICommentProcessor) processor).setParagraph(paragraph); } try { - expressionResolver.resolveExpression(strippedExpression, - expressionContext); - placeholderReplacer.replace(paragraphWrapper, - processorExpression, ""); + expressionResolver.resolve(expression, expressionContext); + placeholderReplacer.replace(paragraphWrapper, expression, ""); logger.debug( "Processor expression '{}' has been successfully processed by a comment processor.", - processorExpression); + expression); } catch (SpelEvaluationException | SpelParseException e) { String msg = "Expression '%s' failed since no processor solves it".formatted( - strippedExpression); + expression); if (failOnUnresolvedExpression) { throw new DocxStamperException(msg, e); } else { @@ -201,7 +203,7 @@ private Optional runCommentProcessors( return Optional.empty(); } - String commentString = CommentUtil.getCommentString(comment); + String commentString = getCommentString(comment); for (final Object processor : commentProcessors.values()) { ((ICommentProcessor) processor).setParagraph(paragraph); @@ -212,8 +214,8 @@ private Optional runCommentProcessors( } try { - expressionResolver.resolveExpression(commentString, - expressionContext); + expressionResolver.resolve( + new Expression(commentString), expressionContext); comments.remove(comment.getId()); logger.debug( "Comment {} has been successfully processed by a comment processor.", diff --git a/src/main/java/org/wickedsource/docxstamper/processor/displayif/DisplayIfProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/displayif/DisplayIfProcessor.java index c9064b97..16314c79 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/displayif/DisplayIfProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/displayif/DisplayIfProcessor.java @@ -35,7 +35,7 @@ private DisplayIfProcessor(PlaceholderReplacer placeholderReplacer) { /** * Creates a new DisplayIfProcessor instance. * - * @param pr the {@link PlaceholderReplacer} to use for replacing placeholders. + * @param pr the {@link PlaceholderReplacer} used for replacing expressions. * @return a new DisplayIfProcessor instance. */ public static ICommentProcessor newInstance(PlaceholderReplacer pr) { diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java index 53319761..4680399c 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java @@ -48,7 +48,7 @@ private ParagraphRepeatProcessor( /** *

newInstance.

* - * @param pr replaces placeholders with values + * @param pr replaces expressions with values * @param nullReplacement replaces null values * @return a new instance of ParagraphRepeatProcessor */ @@ -59,7 +59,7 @@ public static ICommentProcessor newInstance(PlaceholderReplacer pr, String nullR /** *

newInstance.

* - * @param placeholderReplacer replaces placeholders with values + * @param placeholderReplacer replaces expressions with values * @return a new instance of ParagraphRepeatProcessor */ public static ICommentProcessor newInstance(PlaceholderReplacer placeholderReplacer) { diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java index 678b057f..951c94fd 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java @@ -7,8 +7,9 @@ import org.wickedsource.docxstamper.util.walk.BaseDocumentWalker; /** - * Walks through a document and replaces placeholders with values from the given expression context. - * This walker only replaces placeholders in paragraphs, not in tables. + * Walks through a document and replaces expressions with values from the given + * expression context. + * This walker only replaces expressions in paragraphs, not in tables. * * @author Joseph Verron * @version ${version} @@ -25,7 +26,7 @@ public class ParagraphResolverDocumentWalker extends BaseDocumentWalker { * @param rowClone The row to start with * @param expressionContext The context of the expressions to resolve * @param document The document to walk through - * @param replacer The placeholder replacer to use for resolving + * @param replacer The placeholderReplacer to use for resolving */ public ParagraphResolverDocumentWalker(Tr rowClone, Object expressionContext, WordprocessingMLPackage document, PlaceholderReplacer replacer) { super(rowClone); diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java index 376e529c..210117d9 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java @@ -42,7 +42,9 @@ * @version ${version} * @since 1.3.0 */ -public class RepeatDocPartProcessor extends BaseCommentProcessor implements IRepeatDocPartProcessor { +public class RepeatDocPartProcessor + extends BaseCommentProcessor + implements IRepeatDocPartProcessor { private static final ThreadFactory threadFactory = Executors.defaultThreadFactory(); private static final ObjectFactory objectFactory = Context.getWmlObjectFactory(); @@ -63,9 +65,10 @@ private RepeatDocPartProcessor( /** *

newInstance.

* - * @param pr the placeholder replacer + * @param pr the placeholderReplacer * @param stamper the stamper - * @param nullReplacementValue the value to use when the placeholder is null + * @param nullReplacementValue the value to use when the expression + * resolves to null * @return a new instance of this processor */ public static ICommentProcessor newInstance( @@ -81,7 +84,7 @@ public static ICommentProcessor newInstance( /** *

newInstance.

* - * @param pr the placeholder replacer + * @param pr the placeholderReplacer * @param stamper the stamper * @return a new instance of this processor */ @@ -216,7 +219,9 @@ private Deque stampSubDocuments( return subDocuments; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public void commitChanges(WordprocessingMLPackage document) { for (Entry> entry : this.contexts.entrySet()) { @@ -292,7 +297,9 @@ private void stamp( stamper.stamp(template, context, outputStream); } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public void reset() { contexts.clear(); diff --git a/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java b/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java index 34bc043a..a7eda652 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java @@ -15,6 +15,7 @@ import org.wickedsource.docxstamper.util.ParagraphWrapper; import org.wickedsource.docxstamper.util.RunUtil; import org.wickedsource.docxstamper.util.walk.BaseCoordinatesWalker; +import pro.verron.docxstamper.core.Expression; import java.util.Optional; @@ -37,7 +38,7 @@ public class PlaceholderReplacer { private final boolean leaveEmptyOnExpressionError; private final boolean replaceUnresolvedExpressions; private final String unresolvedExpressionsDefaultValue; - private final String lineBreakPlaceholder; + private final Expression lineBreakPlaceholder; /** *

Constructor for PlaceholderReplacer.

@@ -53,7 +54,8 @@ public class PlaceholderReplacer { * that cannot be resolved will * be by replaced by an * empty string. - * @param lineBreakPlaceholder if set to a non-null value, all occurrences of this placeholder will be + * @param lineBreakExpression if set to a non-null value, + * all occurrences of this placeholder will be * replaced with a line break. */ public PlaceholderReplacer( @@ -63,7 +65,7 @@ public PlaceholderReplacer( boolean replaceUnresolvedExpressions, String unresolvedExpressionsDefaultValue, boolean leaveEmptyOnExpressionError, - String lineBreakPlaceholder + Expression lineBreakExpression ) { this.registry = registry; this.resolver = resolver; @@ -71,7 +73,7 @@ public PlaceholderReplacer( this.replaceUnresolvedExpressions = replaceUnresolvedExpressions; this.unresolvedExpressionsDefaultValue = unresolvedExpressionsDefaultValue; this.leaveEmptyOnExpressionError = leaveEmptyOnExpressionError; - this.lineBreakPlaceholder = lineBreakPlaceholder; + this.lineBreakPlaceholder = lineBreakExpression; } /** @@ -108,33 +110,32 @@ public void resolveExpressionsForParagraph( WordprocessingMLPackage document ) { var paragraphWrapper = new ParagraphWrapper(p); - var placeholders = findVariableExpressions(paragraphWrapper.getText()); - for (var placeholder : placeholders) { + var expressions = findVariableExpressions(paragraphWrapper.getText()); + for (var expression : expressions) { try { - var resolution = resolver.resolveExpression(placeholder, - context); + var resolution = resolver.resolve(expression, context); var replacement = registry.resolve(document, - placeholder, + expression, resolution); - replace(paragraphWrapper, placeholder, replacement); + replace(paragraphWrapper, expression, replacement); } catch (SpelEvaluationException | SpelParseException e) { if (failOnUnresolvedExpression) { String message = "Expression %s could not be resolved against context of type %s" - .formatted(placeholder, + .formatted(expression, context.getClass()); throw new DocxStamperException(message, e); } else { log.warn( "Expression {} could not be resolved against context root of type {}. Reason: {}. Set log level to TRACE to view Stacktrace.", - placeholder, + expression, context.getClass(), e.getMessage()); log.trace("Reason for skipping expression:", e); if (leaveEmptyOnExpressionError) { - replace(paragraphWrapper, placeholder, ""); + replace(paragraphWrapper, expression, ""); } else if (replaceUnresolvedExpressions()) { replace(paragraphWrapper, - placeholder, + expression, unresolvedExpressionsDefaultValue()); } } @@ -147,10 +148,10 @@ public void resolveExpressionsForParagraph( private void replace( ParagraphWrapper p, - String placeholder, + Expression expression, R replacementRun ) { - p.replace(placeholder, + p.replace(expression, replacementRun == null ? RunUtil.create("") : replacementRun); } @@ -158,19 +159,19 @@ private void replace( * Replaces expressions in the given paragraph and replaces them with the values provided by the expression resolver. * * @param p the paragraph in which to replace expressions. - * @param placeholder the placeholder to replace. - * @param replacementObject the object to replace the placeholder with. + * @param expression the expression to replace. + * @param replacementObject the object to replace the expression with. */ public void replace( ParagraphWrapper p, - String placeholder, + Expression expression, String replacementObject ) { Optional.ofNullable(replacementObject) .map(replacementStr -> RunUtil.create(replacementStr, p.getParagraph())) .ifPresent(replacementRun -> replace(p, - placeholder, + expression, replacementRun)); } @@ -182,7 +183,7 @@ private String unresolvedExpressionsDefaultValue() { return unresolvedExpressionsDefaultValue; } - private String lineBreakPlaceholder() { + private Expression lineBreakPlaceholder() { return lineBreakPlaceholder; } @@ -191,7 +192,7 @@ private void replaceLineBreaks(ParagraphWrapper paragraphWrapper) { .createBr(); R run = RunUtil.create(lineBreak); while (paragraphWrapper.getText() - .contains(lineBreakPlaceholder())) { + .contains(lineBreakPlaceholder().inner())) { replace(paragraphWrapper, lineBreakPlaceholder(), run); } } diff --git a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/LegacyFallbackResolver.java b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/LegacyFallbackResolver.java index a7203e1b..47875eba 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/LegacyFallbackResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/LegacyFallbackResolver.java @@ -37,7 +37,7 @@ public boolean canResolve(Object object) { @Override public R resolve( WordprocessingMLPackage document, - String placeholder, + String expression, Object object ) { return RunUtil.create(format(object)); diff --git a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/ObjectResolverRegistry.java b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/ObjectResolverRegistry.java index d7bc3993..a6c99dca 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/ObjectResolverRegistry.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/ObjectResolverRegistry.java @@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory; import org.wickedsource.docxstamper.api.DocxStamperException; import pro.verron.docxstamper.api.ObjectResolver; +import pro.verron.docxstamper.core.Expression; import java.util.ArrayList; import java.util.List; @@ -33,24 +34,24 @@ public ObjectResolverRegistry(List resolvers) { } /** - * Resolves the placeholder in the given document with the provided object. + * Resolves the expression in the given document with the provided object. * - * @param document the WordprocessingMLPackage document in which to resolve the placeholder - * @param placeholder the placeholder value to be replaced - * @param object the object to be used for resolving the placeholder - * @return the resolved value for the placeholder + * @param document the WordprocessingMLPackage document in which to resolve the placeholder + * @param expression the expression value to be replaced + * @param object the object to be used for resolving the expression + * @return the resolved value for the expression * @throws DocxStamperException if no resolver is found for the object */ public R resolve( WordprocessingMLPackage document, - String placeholder, + Expression expression, Object object ) { for (ObjectResolver resolver : resolvers) if (resolver.canResolve(object)) { - R resolution = resolver.resolve(document, placeholder, object); + R resolution = resolver.resolve(document, expression, object); var msg = "Expression '{}' replaced by '{}' with resolver {}"; - log.debug(msg, placeholder, resolution, resolver); + log.debug(msg, expression, resolution, resolver); return resolution; } String message = "No resolver found for %s".formatted(object); diff --git a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/TypeResolver.java b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/TypeResolver.java index df06ed25..39a29533 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/TypeResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/TypeResolver.java @@ -14,13 +14,12 @@ * @param resolver the resolver to resolve objects of the given type. * @param nullProof a boolean value indicating whether the resolver is null-proof. * @param the type of the object this TypeResolver is responsible for resolving. - * @deprecated This class's been deprecated since version 1.6.7 - * and will be removed in a future release. - * Use the {@link ObjectResolver} interface instead. - * * @author Joseph Verron * @version ${version} * @since 1.6.7 + * @deprecated This class's been deprecated since version 1.6.7 + * and will be removed in a future release. + * Use the {@link ObjectResolver} interface instead. */ @Deprecated(since = "1.6.7", forRemoval = true) public record TypeResolver( @@ -59,16 +58,17 @@ public boolean canResolve(Object object) { /** * Resolves an object of a specified type to an object of the DOCX4J API that can be placed in a .docx document. * - * @param document the WordprocessingMLPackage object representing the .docx document - * @param placeholder the placeholder string to be replaced in the .docx document - * @param object the object to be resolved - * @return an object of the DOCX4J API that replaces the placeholder in the .docx document + * @param document the WordprocessingMLPackage object representing the .docx document + * @param expression the expression to be replaced in the .docx document + * @param object the object to be resolved + * @return an object of the DOCX4J API that replaces the expression in the + * .docx document * @throws DocxStamperException if the object is not an instance of the specified type */ @Override public R resolve( WordprocessingMLPackage document, - String placeholder, + String expression, Object object ) { if (type.isInstance(object)) diff --git a/src/main/java/org/wickedsource/docxstamper/util/ParagraphWrapper.java b/src/main/java/org/wickedsource/docxstamper/util/ParagraphWrapper.java index b1faaabe..427db00a 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/ParagraphWrapper.java +++ b/src/main/java/org/wickedsource/docxstamper/util/ParagraphWrapper.java @@ -2,6 +2,7 @@ import org.docx4j.wml.P; import org.docx4j.wml.R; +import pro.verron.docxstamper.core.Expression; import java.util.ArrayList; import java.util.List; @@ -24,164 +25,190 @@ * @since 1.0.8 */ public class ParagraphWrapper { - private final List runs = new ArrayList<>(); - private final P paragraph; - private int currentPosition = 0; - - /** - * Constructs a new ParagraphWrapper for the given paragraph. - * - * @param paragraph the paragraph to wrap. - */ - public ParagraphWrapper(P paragraph) { - this.paragraph = paragraph; - recalculateRuns(); - } - - /** - * Recalculates the runs of the paragraph. This method is called automatically by the constructor, but can also be - * called manually to recalculate the runs after a modification to the paragraph was done. - */ - public void recalculateRuns() { - currentPosition = 0; - this.runs.clear(); - int index = 0; - for (Object contentElement : paragraph.getContent()) { - if (contentElement instanceof R r && !RunUtil.getText(r).isEmpty()) { - this.addRun(r, index); - } - index++; - } - } - - /** - * Adds a run to the aggregation. - * - * @param run the run to add. - */ - private void addRun(R run, int index) { - int startIndex = currentPosition; - int endIndex = currentPosition + RunUtil.getText(run).length() - 1; - runs.add(new IndexedRun(startIndex, endIndex, index, run)); - currentPosition = endIndex + 1; - } - - /** - * Replaces the given placeholder String with the replacement object within the paragraph. - * The replacement object must be a valid DOCX4J Object. - * - * @param placeholder the placeholder to be replaced. - * @param replacement the object to replace the placeholder String. - */ - public void replace(String placeholder, R replacement) { - String text = getText(); - int matchStartIndex = text.indexOf(placeholder); - if (matchStartIndex == -1) { - // nothing to replace - return; - } - int matchEndIndex = matchStartIndex + placeholder.length() - 1; - List affectedRuns = getAffectedRuns(matchStartIndex, matchEndIndex); - - boolean singleRun = affectedRuns.size() == 1; - - if (singleRun) { - IndexedRun run = affectedRuns.get(0); - - - boolean placeholderSpansCompleteRun = placeholder.length() == RunUtil.getText(run.run()).length(); - boolean placeholderAtStartOfRun = matchStartIndex == run.startIndex(); - boolean placeholderAtEndOfRun = matchEndIndex == run.endIndex(); - boolean placeholderWithinRun = matchStartIndex > run.startIndex() && matchEndIndex < run.endIndex(); - - replacement.setRPr(run.run().getRPr()); - - if (placeholderSpansCompleteRun) { - this.paragraph.getContent().remove(run.run()); - this.paragraph.getContent().add(run.indexInParent(), replacement); - recalculateRuns(); - } else if (placeholderAtStartOfRun) { - run.replace(matchStartIndex, matchEndIndex, ""); - this.paragraph.getContent().add(run.indexInParent(), replacement); - recalculateRuns(); - } else if (placeholderAtEndOfRun) { - run.replace(matchStartIndex, matchEndIndex, ""); - this.paragraph.getContent().add(run.indexInParent() + 1, replacement); - recalculateRuns(); - } else if (placeholderWithinRun) { + private final List runs = new ArrayList<>(); + private final P paragraph; + private int currentPosition = 0; + + /** + * Constructs a new ParagraphWrapper for the given paragraph. + * + * @param paragraph the paragraph to wrap. + */ + public ParagraphWrapper(P paragraph) { + this.paragraph = paragraph; + recalculateRuns(); + } + + /** + * Recalculates the runs of the paragraph. This method is called automatically by the constructor, but can also be + * called manually to recalculate the runs after a modification to the paragraph was done. + */ + public void recalculateRuns() { + currentPosition = 0; + this.runs.clear(); + int index = 0; + for (Object contentElement : paragraph.getContent()) { + if (contentElement instanceof R r && !RunUtil.getText(r) + .isEmpty()) { + this.addRun(r, index); + } + index++; + } + } + + /** + * Adds a run to the aggregation. + * + * @param run the run to add. + */ + private void addRun(R run, int index) { + int startIndex = currentPosition; + int endIndex = currentPosition + RunUtil.getText(run) + .length() - 1; + runs.add(new IndexedRun(startIndex, endIndex, index, run)); + currentPosition = endIndex + 1; + } + + /** + * Replaces the given expression with the replacement object within + * the paragraph. + * The replacement object must be a valid DOCX4J Object. + * + * @param expression the expression to be replaced. + * @param replacement the object to replace the expression. + */ + public void replace(Expression expression, R replacement) { + String text = getText(); + String full = expression.expression(); + int matchStartIndex = text.indexOf(full); + if (matchStartIndex == -1) { + // nothing to replace + return; + } + int matchEndIndex = matchStartIndex + full.length() - 1; + List affectedRuns = getAffectedRuns(matchStartIndex, + matchEndIndex); + + boolean singleRun = affectedRuns.size() == 1; + + if (singleRun) { + IndexedRun run = affectedRuns.get(0); + + + boolean expressionSpansCompleteRun = + full.length() == RunUtil.getText(run.run()) + .length(); + boolean expressionAtStartOfRun = matchStartIndex == run.startIndex(); + boolean expressionAtEndOfRun = matchEndIndex == run.endIndex(); + boolean expressionWithinRun = matchStartIndex > run.startIndex() && matchEndIndex < run.endIndex(); + + replacement.setRPr(run.run() + .getRPr()); + + if (expressionSpansCompleteRun) { + this.paragraph.getContent() + .remove(run.run()); + this.paragraph.getContent() + .add(run.indexInParent(), replacement); + recalculateRuns(); + } else if (expressionAtStartOfRun) { + run.replace(matchStartIndex, matchEndIndex, ""); + this.paragraph.getContent() + .add(run.indexInParent(), replacement); + recalculateRuns(); + } else if (expressionAtEndOfRun) { + run.replace(matchStartIndex, matchEndIndex, ""); + this.paragraph.getContent() + .add(run.indexInParent() + 1, replacement); + recalculateRuns(); + } else if (expressionWithinRun) { String runText = RunUtil.getText(run.run()); - int startIndex = runText.indexOf(placeholder); - int endIndex = startIndex + placeholder.length(); - R run1 = RunUtil.create(runText.substring(0, startIndex), this.paragraph); - R run2 = RunUtil.create(runText.substring(endIndex), this.paragraph); - this.paragraph.getContent().add(run.indexInParent(), run2); - this.paragraph.getContent().add(run.indexInParent(), replacement); - this.paragraph.getContent().add(run.indexInParent(), run1); - this.paragraph.getContent().remove(run.run()); - recalculateRuns(); - } - } else { - IndexedRun firstRun = affectedRuns.get(0); - IndexedRun lastRun = affectedRuns.get(affectedRuns.size() - 1); - replacement.setRPr(firstRun.run().getRPr()); - // remove the placeholder from first and last run - firstRun.replace(matchStartIndex, matchEndIndex, ""); - lastRun.replace(matchStartIndex, matchEndIndex, ""); - - // remove all runs between first and last - for (IndexedRun run : affectedRuns) { - if (!Objects.equals(run, firstRun) && !Objects.equals(run, lastRun)) { - this.paragraph.getContent().remove(run.run()); - } - } - - // add replacement run between first and last run - this.paragraph.getContent().add(firstRun.indexInParent() + 1, replacement); - - recalculateRuns(); - } - } - - /** - * Returns the aggregated text over all runs. - * - * @return the text of all runs. - */ - public String getText() { - return runs.stream() + int startIndex = runText.indexOf(full); + int endIndex = startIndex + full.length(); + R run1 = RunUtil.create(runText.substring(0, startIndex), + this.paragraph); + R run2 = RunUtil.create(runText.substring(endIndex), + this.paragraph); + this.paragraph.getContent() + .add(run.indexInParent(), run2); + this.paragraph.getContent() + .add(run.indexInParent(), replacement); + this.paragraph.getContent() + .add(run.indexInParent(), run1); + this.paragraph.getContent() + .remove(run.run()); + recalculateRuns(); + } + } else { + IndexedRun firstRun = affectedRuns.get(0); + IndexedRun lastRun = affectedRuns.get(affectedRuns.size() - 1); + replacement.setRPr(firstRun.run() + .getRPr()); + // remove the expression from first and last run + firstRun.replace(matchStartIndex, matchEndIndex, ""); + lastRun.replace(matchStartIndex, matchEndIndex, ""); + + // remove all runs between first and last + for (IndexedRun run : affectedRuns) { + if (!Objects.equals(run, firstRun) && !Objects.equals(run, + lastRun)) { + this.paragraph.getContent() + .remove(run.run()); + } + } + + // add replacement run between first and last run + this.paragraph.getContent() + .add(firstRun.indexInParent() + 1, replacement); + + recalculateRuns(); + } + } + + /** + * Returns the aggregated text over all runs. + * + * @return the text of all runs. + */ + public String getText() { + return runs.stream() .map(IndexedRun::run) - .map(RunUtil::getText) - .collect(joining()); - } - - private List getAffectedRuns(int startIndex, int endIndex) { - return runs.stream() - .filter(run -> run.isTouchedByRange(startIndex, endIndex)) - .toList(); - } - - /** - * Returns the list of runs that are aggregated. Depending on what modifications were done to the aggregated text - * this list may not return the same runs initially added to the aggregator. - * - * @return the list of aggregated runs. - */ - public List getRuns() { - return runs.stream().map(IndexedRun::run).toList(); - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return getText(); - } - - /** - *

Getter for the field paragraph.

- * - * @return a {@link P} object - */ - public P getParagraph() { - return paragraph; - } + .map(RunUtil::getText) + .collect(joining()); + } + + private List getAffectedRuns(int startIndex, int endIndex) { + return runs.stream() + .filter(run -> run.isTouchedByRange(startIndex, endIndex)) + .toList(); + } + + /** + * Returns the list of runs that are aggregated. Depending on what modifications were done to the aggregated text + * this list may not return the same runs initially added to the aggregator. + * + * @return the list of aggregated runs. + */ + public List getRuns() { + return runs.stream() + .map(IndexedRun::run) + .toList(); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return getText(); + } + + /** + *

Getter for the field paragraph.

+ * + * @return a {@link P} object + */ + public P getParagraph() { + return paragraph; + } } diff --git a/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java b/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java index a9fa2370..e3588106 100644 --- a/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java +++ b/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java @@ -3,6 +3,7 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; import org.wickedsource.docxstamper.api.DocxStamperException; +import pro.verron.docxstamper.core.Expression; /** * The ObjectResolver interface provides a contract for resolving objects to create a run @@ -23,17 +24,36 @@ public interface ObjectResolver { boolean canResolve(Object object); /** - * Resolves the placeholder in the given document with the provided object. + * Resolves the expression in the given document with the provided object. * - * @param document the {@link WordprocessingMLPackage} document in which to resolve the placeholder - * @param placeholder the placeholder value to be replaced - * @param object the object to be used for resolving the placeholder - * @return the resolved value for the placeholder + * @param document the {@link WordprocessingMLPackage} document in + * which to resolve the expression + * @param expression the expression value to be replaced + * @param object the object to be used for resolving the expression + * @return the resolved value for the expression + * @throws DocxStamperException if no resolver is found for the object + */ + default R resolve( + WordprocessingMLPackage document, + Expression expression, + Object object + ) { + return resolve(document, expression.inner(), object); + } + + /** + * Resolves the expression in the given document with the provided object. + * + * @param document the {@link WordprocessingMLPackage} document in + * which to resolve the expression + * @param expression the expression value to be replaced + * @param object the object to be used for resolving the expression + * @return the resolved value for the expression * @throws DocxStamperException if no resolver is found for the object */ R resolve( WordprocessingMLPackage document, - String placeholder, + String expression, Object object ); } diff --git a/src/main/java/pro/verron/docxstamper/core/Expression.java b/src/main/java/pro/verron/docxstamper/core/Expression.java new file mode 100644 index 00000000..1de06dce --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/core/Expression.java @@ -0,0 +1,53 @@ +package pro.verron.docxstamper.core; + +import org.springframework.lang.NonNull; + +import java.util.Objects; + +public final class Expression { + private static final Matcher DEFAULT_MATCHER = new Matcher("${", "}"); + private static final Matcher SECONDARY_MATCHER = new Matcher("#{", "}"); + @NonNull + private final String expression; + + public Expression(@NonNull String expression) { + this.expression = expression; + } + + /** + * Cleans the given expression by stripping the prefix and suffix if they match any of the configured matchers. + * + * @return the cleaned expression. + */ + public String inner() { + if (DEFAULT_MATCHER.match(expression)) + return DEFAULT_MATCHER.strip(expression); + if (SECONDARY_MATCHER.match(expression)) + return SECONDARY_MATCHER.strip(expression); + return expression; + } + + @NonNull + public String expression() {return expression;} + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (Expression) obj; + return Objects.equals(this.expression, that.expression); + } + + @Override + public int hashCode() { + return Objects.hash(expression); + } + + @Override + public String toString() { + return "Expression[" + + "expression=" + expression + ']'; + } + + +} diff --git a/src/main/java/org/wickedsource/docxstamper/el/Matcher.java b/src/main/java/pro/verron/docxstamper/core/Matcher.java similarity index 95% rename from src/main/java/org/wickedsource/docxstamper/el/Matcher.java rename to src/main/java/pro/verron/docxstamper/core/Matcher.java index aa5713d3..25b26d2a 100644 --- a/src/main/java/org/wickedsource/docxstamper/el/Matcher.java +++ b/src/main/java/pro/verron/docxstamper/core/Matcher.java @@ -1,4 +1,4 @@ -package org.wickedsource.docxstamper.el; +package pro.verron.docxstamper.core; import org.springframework.lang.NonNull; diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java index 4a48e4ff..827deccd 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java @@ -120,7 +120,7 @@ public R resolve(WordprocessingMLPackage document, Image image) { @Override public R resolve( WordprocessingMLPackage document, - String placeholder, + String expression, Object object ) { if (object instanceof Image image) diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/Null2DefaultResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/Null2DefaultResolver.java index d5a19f4a..bb2ca14c 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/Null2DefaultResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/Null2DefaultResolver.java @@ -31,7 +31,7 @@ public boolean canResolve(Object object) { @Override public R resolve( WordprocessingMLPackage document, - String placeholder, + String expression, Object object ) { return RunUtil.create(text); diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/Null2PlaceholderResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/Null2PlaceholderResolver.java index 47673f12..3f0873c2 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/Null2PlaceholderResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/Null2PlaceholderResolver.java @@ -2,12 +2,14 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; +import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.util.RunUtil; import pro.verron.docxstamper.api.ObjectResolver; +import pro.verron.docxstamper.core.Expression; /** * The {@link Null2PlaceholderResolver} class is an implementation of the ObjectResolver interface. - * It provides a way to resolve null objects by not replacing their placeholder string. + * It provides a way to resolve null objects by not replacing their expression. * * @author Joseph Verron * @version ${version} @@ -28,9 +30,18 @@ public boolean canResolve(Object object) { @Override public R resolve( WordprocessingMLPackage document, - String placeholder, + Expression expression, Object object ) { - return RunUtil.create(placeholder); + return RunUtil.create(expression.expression()); + } + + @Override + public R resolve( + WordprocessingMLPackage document, + String expression, + Object object + ) { + throw new DocxStamperException("Should not be called"); } } diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/Resolvers.java b/src/main/java/pro/verron/docxstamper/preset/resolver/Resolvers.java index 529b8f61..6f600077 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/Resolvers.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/Resolvers.java @@ -52,7 +52,7 @@ public static ObjectResolver nullToDefault(String value) { /** * Returns an instance of {@link ObjectResolver} that resolves null objects - * by not replacing their placeholder string. + * by not replacing their expression. * * @return An instance of {@link ObjectResolver} */ diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/StringResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/StringResolver.java index a5355363..8c284976 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/StringResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/StringResolver.java @@ -45,14 +45,16 @@ public final boolean canResolve(Object object) { /** * Resolves an object to a string and creates a new run with the resolved string as content. * - * @param document the WordprocessingMLPackage document - * @param placeholder the placeholder string - * @param object the object to be resolved + * @param document the WordprocessingMLPackage document + * @param expression the expression string + * @param object the object to be resolved * @return the newly created run with the resolved string as content */ @Override public final R resolve( - WordprocessingMLPackage document, String placeholder, Object object + WordprocessingMLPackage document, + String expression, + Object object ) { return RunUtil.create(resolve(type.cast(object))); } diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/ToStringResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/ToStringResolver.java index 066ebc47..c3050867 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/ToStringResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/ToStringResolver.java @@ -24,7 +24,7 @@ public boolean canResolve(Object object) { @Override public R resolve( WordprocessingMLPackage document, - String placeholder, + String expression, Object object ) { return RunUtil.create(String.valueOf(object)); diff --git a/src/test/java/org/wickedsource/docxstamper/el/ExpressionUtilTest.java b/src/test/java/org/wickedsource/docxstamper/el/ExpressionUtilTest.java deleted file mode 100644 index ac4a30f0..00000000 --- a/src/test/java/org/wickedsource/docxstamper/el/ExpressionUtilTest.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.wickedsource.docxstamper.el; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; -import static org.wickedsource.docxstamper.el.ExpressionResolver.cleanExpression; -import static org.wickedsource.docxstamper.el.ExpressionUtil.findVariableExpressions; - -/** - * @author Joseph Verron - * @author Tom Hombergs - */ -@DisplayName("Utilities - Expression finder") -class ExpressionUtilTest { - @Test - void findsPlaceholders() { - String text = "lorem ipsum ${placeholder1} lorem ipsum ${placeholder2}"; - - List placeholders = findVariableExpressions(text); - - assertEquals(2, placeholders.size()); - assertEquals("${placeholder1}", placeholders.get(0)); - assertEquals("${placeholder2}", placeholders.get(1)); - } - - @Test - void findsProcessorExpressions() { - String text = "lorem ipsum #{expression1} lorem ipsum #{expression2}"; - - List placeholders = ExpressionUtil.findProcessorExpressions( - text); - - assertEquals(2, placeholders.size()); - assertEquals("#{expression1}", placeholders.get(0)); - assertEquals("#{expression2}", placeholders.get(1)); - } - - @Test - void findsPlaceholdersWithError() { - String text = "lorem ipsum ${placeholder1} ${ lorem ipsum } ${placeholder2"; - - List placeholders = findVariableExpressions(text); - - assertEquals(2, placeholders.size()); - assertEquals("${placeholder1}", placeholders.get(0)); - assertEquals("${ lorem ipsum }", placeholders.get(1)); - } - - @Test - void returnsEmptyListOnEmptyText() { - String text = ""; - List placeholders = findVariableExpressions(text); - assertTrue(placeholders.isEmpty()); - } - - @Test - void stripsExpressions() { - String expressionValue = "myExpression"; - String expression = "${%s}".formatted(expressionValue); - - String actual = cleanExpression(expression); - - assertEquals(expressionValue, actual); - } - -} diff --git a/src/test/java/org/wickedsource/docxstamper/replace/ParagraphWrapperTest.java b/src/test/java/org/wickedsource/docxstamper/replace/ParagraphWrapperTest.java index 09666028..62ee7670 100644 --- a/src/test/java/org/wickedsource/docxstamper/replace/ParagraphWrapperTest.java +++ b/src/test/java/org/wickedsource/docxstamper/replace/ParagraphWrapperTest.java @@ -33,60 +33,4 @@ void getRunsReturnsAddedRuns() { assertEquals(" ", RunUtil.getText(aggregator.getRuns().get(1))); assertEquals("ipsum", RunUtil.getText(aggregator.getRuns().get(2))); } - - @Test - void placeholderSpansFullSingleRun() { - ParagraphWrapper wrapper = loremIpsum(); - wrapper.replace("lorem", RunUtil.create("")); - assertEquals(" ipsum", wrapper.getText()); - } - - @Test - void placeholderWithinSingleRun() { - ParagraphWrapper wrapper = new ParagraphWrapper(create("My name is ${name}.")); - wrapper.replace("${name}", RunUtil.create("Bob")); - assertEquals("My name is Bob.", wrapper.getText()); - } - - @Test - void placeholderAtStartOfSingleRun() { - ParagraphWrapper wrapper = new ParagraphWrapper(create("${name} my name is.")); - wrapper.replace("${name}", RunUtil.create("Yoda")); - assertEquals("Yoda my name is.", wrapper.getText()); - } - - @Test - void placeholderAtEndOfSingleRun() { - ParagraphWrapper wrapper = new ParagraphWrapper(create("My name is ${name}")); - wrapper.replace("${name}", RunUtil.create("Yoda")); - assertEquals("My name is Yoda", wrapper.getText()); - } - - @Test - void placeholderWithinMultipleRuns() { - ParagraphWrapper wrapper = new ParagraphWrapper(create("My name is ${", "name", "}.")); - wrapper.replace("${name}", RunUtil.create("Yoda")); - assertEquals("My name is Yoda.", wrapper.getText()); - } - - @Test - void placeholderStartsWithinMultipleRuns() { - ParagraphWrapper wrapper = new ParagraphWrapper(create("${", "name", "} my name is.")); - wrapper.replace("${name}", RunUtil.create("Yoda")); - assertEquals("Yoda my name is.", wrapper.getText()); - } - - @Test - void placeholderEndsWithinMultipleRuns() { - ParagraphWrapper wrapper = new ParagraphWrapper(create("My name is ${", "name", "}")); - wrapper.replace("${name}", RunUtil.create("Yoda")); - assertEquals("My name is Yoda", wrapper.getText()); - } - - @Test - void placeholderExactlySpansMultipleRuns() { - ParagraphWrapper wrapper = new ParagraphWrapper(create("${", "name", "}")); - wrapper.replace("${name}", RunUtil.create("Yoda")); - assertEquals("Yoda", wrapper.getText()); - } } From ada8761d73f4e6a675d2e1567c3fa5fe20a3dd00 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 14 Mar 2024 01:01:18 +0100 Subject: [PATCH 004/134] Refactor code to use new ExpressionFinder class and update comment processing The current codebase has been substantially refactored to transition away from using static methods for finding expressions in text, to instead use a new ExpressionFinder class. This results in a more object-oriented design. As part of this change, the utility classes 'ExpressionUtil' and 'CommentUtilTest' were deleted. The processing of comments was also substantially updated to use these new objects, resulting in cleaner, more readable code. --- .../wickedsource/docxstamper/DocxStamper.java | 3 +- .../docxstamper/el/ExpressionUtil.java | 70 ------------------- .../processor/CommentProcessorRegistry.java | 18 ++--- .../replace/PlaceholderReplacer.java | 9 ++- .../docxstamper/util/CommentUtil.java | 11 +-- .../verron/docxstamper/core/Expression.java | 48 ++----------- .../docxstamper/core/ExpressionFinder.java | 36 ++++++++++ .../verron/docxstamper/core/Expressions.java | 38 ++++++++++ .../docxstamper/util/CommentUtilTest.java | 38 ---------- 9 files changed, 102 insertions(+), 169 deletions(-) delete mode 100644 src/main/java/org/wickedsource/docxstamper/el/ExpressionUtil.java create mode 100644 src/main/java/pro/verron/docxstamper/core/ExpressionFinder.java create mode 100644 src/main/java/pro/verron/docxstamper/core/Expressions.java delete mode 100644 src/test/java/org/wickedsource/docxstamper/util/CommentUtilTest.java diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java index 8d806512..12981a6d 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java @@ -18,6 +18,7 @@ import pro.verron.docxstamper.StamperFactory; import pro.verron.docxstamper.api.ObjectResolver; import pro.verron.docxstamper.core.Expression; +import pro.verron.docxstamper.core.Matcher; import java.io.InputStream; import java.io.OutputStream; @@ -114,7 +115,7 @@ private DocxStamper( replaceUnresolvedExpressions, unresolvedExpressionsDefaultValue, leaveEmptyOnExpressionError, - new Expression(lineBreakPlaceholder) + new Expression(new Matcher("", ""), lineBreakPlaceholder) ); for (var entry : configurationCommentProcessors.entrySet()) { diff --git a/src/main/java/org/wickedsource/docxstamper/el/ExpressionUtil.java b/src/main/java/org/wickedsource/docxstamper/el/ExpressionUtil.java deleted file mode 100644 index 6a6da6b7..00000000 --- a/src/main/java/org/wickedsource/docxstamper/el/ExpressionUtil.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.wickedsource.docxstamper.el; - -import org.springframework.lang.NonNull; -import org.wickedsource.docxstamper.api.DocxStamperException; -import pro.verron.docxstamper.core.Expression; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static java.util.Collections.emptyList; - -/** - * Utility class for working with expressions in a text. - * - * @author Joseph Verron - * @author Tom Hombergs - * @version ${version} - * @since 1.0.0 - */ -public class ExpressionUtil { - private static final Pattern VARIABLE_EXPRESSION_PATTERN = Pattern.compile("\\$\\{(.*?)}"); - private static final Pattern PROCESSOR_EXPRESSION_PATTERN = Pattern.compile("#\\{(.*?)}"); - - private ExpressionUtil() { - throw new DocxStamperException("Utility classes should not be instantiated!"); - } - - /** - * Finds all variable expressions in a text and returns them as a list. - * Example expression: "${myObject.property}". - * - * @param text the text to find expressions in. - * @return a list of expressions (including the starting "${" and trailing "}"). - */ - public static List findVariableExpressions(@NonNull String text) { - return findExpressions(text, VARIABLE_EXPRESSION_PATTERN); - } - - private static List findExpressions( - @NonNull String text, - Pattern pattern - ) { - if (text.isEmpty()) - return emptyList(); - Matcher matcher = pattern.matcher(text); - int index = 0; - List matches = new ArrayList<>(); - while (matcher.find(index)) { - String match = matcher.group(); - matches.add(new Expression(match)); - index = matcher.end(); - } - return matches; - } - - /** - * Finds all processor expressions in a text and returns them as a list. - * Example expression: "#{myObject.property}". - * - * @param text the text to find expressions in. - * @return a list of expressions (including the starting "#{" and trailing "}"). - */ - public static List findProcessorExpressions(@NonNull String text) { - return findExpressions(text, PROCESSOR_EXPRESSION_PATTERN); - } - - -} diff --git a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java index 71bb116d..d70d55bb 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java @@ -18,7 +18,7 @@ import org.wickedsource.docxstamper.util.CommentWrapper; import org.wickedsource.docxstamper.util.ParagraphWrapper; import org.wickedsource.docxstamper.util.walk.BaseCoordinatesWalker; -import pro.verron.docxstamper.core.Expression; +import pro.verron.docxstamper.core.Expressions; import java.math.BigInteger; import java.util.ArrayList; @@ -26,7 +26,6 @@ import java.util.Objects; import java.util.Optional; -import static org.wickedsource.docxstamper.el.ExpressionUtil.findProcessorExpressions; import static org.wickedsource.docxstamper.util.CommentUtil.getCommentString; import static org.wickedsource.docxstamper.util.CommentUtil.getComments; @@ -163,7 +162,8 @@ private void runProcessorsOnInlineContent( P paragraph ) { var paragraphWrapper = new ParagraphWrapper(paragraph); - var expressions = findProcessorExpressions(paragraphWrapper.getText()); + String text = paragraphWrapper.getText(); + var expressions = Expressions.findProcessors(text); for (var expression : expressions) { for (final Object processor : commentProcessors.values()) { @@ -203,7 +203,7 @@ private Optional runCommentProcessors( return Optional.empty(); } - String commentString = getCommentString(comment); + var commentExpression = getCommentString(comment); for (final Object processor : commentProcessors.values()) { ((ICommentProcessor) processor).setParagraph(paragraph); @@ -214,20 +214,20 @@ private Optional runCommentProcessors( } try { - expressionResolver.resolve( - new Expression(commentString), expressionContext); + expressionResolver.resolve(commentExpression, expressionContext); comments.remove(comment.getId()); logger.debug( "Comment {} has been successfully processed by a comment processor.", - commentString); + commentExpression); return Optional.of(commentWrapper); } catch (SpelEvaluationException | SpelParseException e) { if (failOnUnresolvedExpression) { - throw new UnresolvedExpressionException(commentString, e); + throw new UnresolvedExpressionException(commentExpression.toString(), + e); } else { logger.warn(String.format( "Skipping comment expression '%s' because it can not be resolved by any comment processor. Reason: %s. Set log level to TRACE to view Stacktrace.", - commentString, + commentExpression, e.getMessage())); logger.trace("Reason for skipping comment: ", e); } diff --git a/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java b/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java index a7eda652..92d3e8a3 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java @@ -16,11 +16,10 @@ import org.wickedsource.docxstamper.util.RunUtil; import org.wickedsource.docxstamper.util.walk.BaseCoordinatesWalker; import pro.verron.docxstamper.core.Expression; +import pro.verron.docxstamper.core.Expressions; import java.util.Optional; -import static org.wickedsource.docxstamper.el.ExpressionUtil.findVariableExpressions; - /** * Replaces expressions in a document with the values provided by the {@link ExpressionResolver}. * @@ -110,12 +109,12 @@ public void resolveExpressionsForParagraph( WordprocessingMLPackage document ) { var paragraphWrapper = new ParagraphWrapper(p); - var expressions = findVariableExpressions(paragraphWrapper.getText()); + String text = paragraphWrapper.getText(); + var expressions = Expressions.findVariables(text); for (var expression : expressions) { try { var resolution = resolver.resolve(expression, context); - var replacement = registry.resolve(document, - expression, + var replacement = registry.resolve(document, expression, resolution); replace(paragraphWrapper, expression, replacement); } catch (SpelEvaluationException | SpelParseException e) { diff --git a/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java b/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java index 70582325..fadc7645 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java +++ b/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java @@ -14,6 +14,8 @@ import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.util.walk.BaseDocumentWalker; import org.wickedsource.docxstamper.util.walk.DocumentWalker; +import pro.verron.docxstamper.core.Expression; +import pro.verron.docxstamper.core.Matcher; import java.math.BigInteger; import java.util.*; @@ -124,11 +126,10 @@ public static Optional findComment( * @param document the document that contains the object. * @return the comment, if found, null otherwise. */ - public static String getCommentStringFor( + public static Expression getCommentStringFor( ContentAccessor object, WordprocessingMLPackage document ) { - Comment comment = getCommentFor(object, - document).orElseThrow(); + Comment comment = getCommentFor(object, document).orElseThrow(); return getCommentString(comment); } @@ -185,14 +186,14 @@ public static Optional getCommentFor( * @param comment a {@link Comment} object * @return a {@link String} object */ - public static String getCommentString(Comment comment) { + public static Expression getCommentString(Comment comment) { StringBuilder builder = new StringBuilder(); for (Object commentChildObject : comment.getContent()) { if (commentChildObject instanceof P p) { builder.append(new ParagraphWrapper(p).getText()); } } - return builder.toString(); + return new Expression(new Matcher("", ""), builder.toString()); } /** diff --git a/src/main/java/pro/verron/docxstamper/core/Expression.java b/src/main/java/pro/verron/docxstamper/core/Expression.java index 1de06dce..ed3bfab7 100644 --- a/src/main/java/pro/verron/docxstamper/core/Expression.java +++ b/src/main/java/pro/verron/docxstamper/core/Expression.java @@ -2,52 +2,18 @@ import org.springframework.lang.NonNull; -import java.util.Objects; - -public final class Expression { - private static final Matcher DEFAULT_MATCHER = new Matcher("${", "}"); - private static final Matcher SECONDARY_MATCHER = new Matcher("#{", "}"); - @NonNull - private final String expression; - - public Expression(@NonNull String expression) { - this.expression = expression; - } - +public record Expression( + @NonNull Matcher matcher, + @NonNull String expression +) { /** * Cleans the given expression by stripping the prefix and suffix if they match any of the configured matchers. * * @return the cleaned expression. */ public String inner() { - if (DEFAULT_MATCHER.match(expression)) - return DEFAULT_MATCHER.strip(expression); - if (SECONDARY_MATCHER.match(expression)) - return SECONDARY_MATCHER.strip(expression); - return expression; - } - - @NonNull - public String expression() {return expression;} - - @Override - public boolean equals(Object obj) { - if (obj == this) return true; - if (obj == null || obj.getClass() != this.getClass()) return false; - var that = (Expression) obj; - return Objects.equals(this.expression, that.expression); + return matcher.match(expression) + ? matcher.strip(expression) + : expression; } - - @Override - public int hashCode() { - return Objects.hash(expression); - } - - @Override - public String toString() { - return "Expression[" + - "expression=" + expression + ']'; - } - - } diff --git a/src/main/java/pro/verron/docxstamper/core/ExpressionFinder.java b/src/main/java/pro/verron/docxstamper/core/ExpressionFinder.java new file mode 100644 index 00000000..ac901b23 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/core/ExpressionFinder.java @@ -0,0 +1,36 @@ +package pro.verron.docxstamper.core; + +import org.springframework.lang.NonNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +import static java.util.Collections.emptyList; + +public class ExpressionFinder { + private final Pattern pattern; + private final Matcher matcher; + + public ExpressionFinder( + Pattern pattern, + Matcher matcher + ) { + this.pattern = pattern; + this.matcher = matcher; + } + + public List find(@NonNull String text) { + if (text.isEmpty()) + return emptyList(); + var matcher = pattern.matcher(text); + int index = 0; + List matches = new ArrayList<>(); + while (matcher.find(index)) { + String match = matcher.group(); + matches.add(new Expression(this.matcher, match)); + index = matcher.end(); + } + return matches; + } +} diff --git a/src/main/java/pro/verron/docxstamper/core/Expressions.java b/src/main/java/pro/verron/docxstamper/core/Expressions.java new file mode 100644 index 00000000..5f079c5c --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/core/Expressions.java @@ -0,0 +1,38 @@ +package pro.verron.docxstamper.core; + +import org.wickedsource.docxstamper.api.DocxStamperException; + +import java.util.List; +import java.util.regex.Pattern; + +/** + * Utility class for working with expressions in a text. + * + * @author Joseph Verron + * @author Tom Hombergs + * @version ${version} + * @since 1.0.0 + */ +public class Expressions { + private static final Pattern PROC_PATTERN = Pattern.compile("#\\{(.*?)}"); + private static final Matcher PROC_MATCHER = new Matcher("#{", "}"); + private static final ExpressionFinder PROC_FINDER = + new ExpressionFinder(PROC_PATTERN, PROC_MATCHER); + private static final Pattern VAR_PATTERN = Pattern.compile("\\$\\{(.*?)}"); + private static final Matcher VAR_MATCHER = new Matcher("${", "}"); + private static final ExpressionFinder VAR_FINDER = + new ExpressionFinder(VAR_PATTERN, VAR_MATCHER); + + private Expressions() { + throw new DocxStamperException( + "Utility classes should not be instantiated!"); + } + + public static List findVariables(String text) { + return VAR_FINDER.find(text); + } + + public static List findProcessors(String text) { + return PROC_FINDER.find(text); + } +} diff --git a/src/test/java/org/wickedsource/docxstamper/util/CommentUtilTest.java b/src/test/java/org/wickedsource/docxstamper/util/CommentUtilTest.java deleted file mode 100644 index f938f0d8..00000000 --- a/src/test/java/org/wickedsource/docxstamper/util/CommentUtilTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.wickedsource.docxstamper.util; - -import org.docx4j.openpackaging.exceptions.Docx4JException; -import org.docx4j.openpackaging.packages.WordprocessingMLPackage; -import org.docx4j.wml.P; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.nio.file.Path; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.wickedsource.docxstamper.DefaultTests.getResource; - -/** - * @author Joseph Verron - * @author Tom Hombergs - */ -@DisplayName("Utilities - Comments") -class CommentUtilTest { - @Test - void onlyParagraphsWithCommentRangeStartAreCommented() throws Docx4JException { - var in = getResource(Path.of("util","CommentUtilTest.docx")); - var document = WordprocessingMLPackage.load(in); - - P p1 = (P) document.getMainDocumentPart().getContent().get(0); - P p2 = (P) document.getMainDocumentPart().getContent().get(1); - P p3 = (P) document.getMainDocumentPart().getContent().get(3); - P p4 = (P) document.getMainDocumentPart().getContent().get(4); - P p5 = (P) document.getMainDocumentPart().getContent().get(5); - - assertTrue(CommentUtil.getCommentFor(p1, document).isEmpty()); - assertEquals("Comment for paragraph 2.", CommentUtil.getCommentStringFor(p2, document)); - assertEquals("Comment for paragraph 3.", CommentUtil.getCommentStringFor(p3, document)); - assertTrue(CommentUtil.getCommentFor(p4, document).isEmpty()); - assertTrue(CommentUtil.getCommentFor(p5, document).isEmpty()); - } -} From 8254663f1aa45561e183be58dcf2f47a0031ee33 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 14 Mar 2024 01:21:59 +0100 Subject: [PATCH 005/134] Refactor ExpressionFinder and update comment management This commit introduces substantial refactorings of the codebase, focusing mainly on the use of a new ExpressionFinder class and updating how comments are handled. The methods previously using static methods for finding expressions have now adopted a more object-oriented approach, while 'ExpressionUtil' and 'CommentUtilTest' were removed. The improved clarity enhances code readability. --- .../docxstamper/util/CommentUtil.java | 8 ++- .../verron/docxstamper/core/Expression.java | 8 ++- .../docxstamper/core/ExpressionFinder.java | 29 ++++++---- .../verron/docxstamper/core/Expressions.java | 57 ++++++++++++++++--- .../pro/verron/docxstamper/core/Matcher.java | 26 ++++++--- 5 files changed, 95 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java b/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java index fadc7645..68f04e97 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java +++ b/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java @@ -120,12 +120,16 @@ public static Optional findComment( } /** - * Returns the comment the given DOCX4J object is commented with. + * Returns the comment string for the given DOCX4J object and document. * * @param object the DOCX4J object whose comment to retrieve. * @param document the document that contains the object. - * @return the comment, if found, null otherwise. + * @return an Expression representing the comment string. + * @throws DocxStamperException if an error occurs while retrieving the comment. + * @deprecated This method's been deprecated since version 1.6.8 + * and will be removed in the future. */ + @Deprecated(since = "1.6.8", forRemoval = true) public static Expression getCommentStringFor( ContentAccessor object, WordprocessingMLPackage document ) { diff --git a/src/main/java/pro/verron/docxstamper/core/Expression.java b/src/main/java/pro/verron/docxstamper/core/Expression.java index ed3bfab7..854a85a0 100644 --- a/src/main/java/pro/verron/docxstamper/core/Expression.java +++ b/src/main/java/pro/verron/docxstamper/core/Expression.java @@ -2,14 +2,18 @@ import org.springframework.lang.NonNull; +/** + * Represents an expression with a configured Matcher. + */ public record Expression( @NonNull Matcher matcher, @NonNull String expression ) { /** - * Cleans the given expression by stripping the prefix and suffix if they match any of the configured matchers. + * Returns the inner part of the expression + * by stripping the prefix and suffix. * - * @return the cleaned expression. + * @return the inner part of the expression. */ public String inner() { return matcher.match(expression) diff --git a/src/main/java/pro/verron/docxstamper/core/ExpressionFinder.java b/src/main/java/pro/verron/docxstamper/core/ExpressionFinder.java index ac901b23..efaeeb79 100644 --- a/src/main/java/pro/verron/docxstamper/core/ExpressionFinder.java +++ b/src/main/java/pro/verron/docxstamper/core/ExpressionFinder.java @@ -8,18 +8,23 @@ import static java.util.Collections.emptyList; -public class ExpressionFinder { - private final Pattern pattern; - private final Matcher matcher; - - public ExpressionFinder( - Pattern pattern, - Matcher matcher - ) { - this.pattern = pattern; - this.matcher = matcher; - } - +/** + * The ExpressionFinder class is responsible + * for finding expressions in a given text based on a specified pattern and matcher. + * It uses the Matcher class + * to determine if an expression matches the specified prefix and suffix, + * and the Expression class to represent each found expression. + */ +public record ExpressionFinder( + Pattern pattern, + Matcher matcher +) { + /** + * Finds expressions in a given text based on a specified pattern and matcher. + * + * @param text the text to search for expressions + * @return a list of found expressions + */ public List find(@NonNull String text) { if (text.isEmpty()) return emptyList(); diff --git a/src/main/java/pro/verron/docxstamper/core/Expressions.java b/src/main/java/pro/verron/docxstamper/core/Expressions.java index 5f079c5c..9a41635c 100644 --- a/src/main/java/pro/verron/docxstamper/core/Expressions.java +++ b/src/main/java/pro/verron/docxstamper/core/Expressions.java @@ -1,25 +1,51 @@ package pro.verron.docxstamper.core; +import org.springframework.lang.NonNull; import org.wickedsource.docxstamper.api.DocxStamperException; import java.util.List; import java.util.regex.Pattern; /** - * Utility class for working with expressions in a text. - * - * @author Joseph Verron - * @author Tom Hombergs - * @version ${version} - * @since 1.0.0 + * The Expressions class provides utility methods + * for finding variables and processors in a given text. */ public class Expressions { + /** + * A regular expression pattern matching processor expressions. + * The pattern search for expressions starting with '#{' and ending with + * '}'. + */ private static final Pattern PROC_PATTERN = Pattern.compile("#\\{(.*?)}"); + /** + * A Matcher matching processor expressions. + * The matcher checks for expressions starting with '#{' and ending with + * '}'. + */ private static final Matcher PROC_MATCHER = new Matcher("#{", "}"); + /** + * An ExpressionFinder to find processor expressions. + * It is initialized with a specified pattern and matcher. + */ private static final ExpressionFinder PROC_FINDER = new ExpressionFinder(PROC_PATTERN, PROC_MATCHER); + + /** + * A regular expression pattern matching processor expressions. + * The pattern search for expressions starting with '${' and ending with + * '}'. + */ private static final Pattern VAR_PATTERN = Pattern.compile("\\$\\{(.*?)}"); + /** + * A Matcher matching processor expressions. + * The matcher checks for expressions starting with '${' and ending with + * '}'. + */ private static final Matcher VAR_MATCHER = new Matcher("${", "}"); + /** + * An ExpressionFinder to find variable expressions. + * It is initialized with a specified pattern and matcher. + */ private static final ExpressionFinder VAR_FINDER = new ExpressionFinder(VAR_PATTERN, VAR_MATCHER); @@ -28,11 +54,26 @@ private Expressions() { "Utility classes should not be instantiated!"); } - public static List findVariables(String text) { + /** + * Finds variable expressions in a given text. + * + * @param text the text to search for variable expressions + * @return a list of found variable expressions as {@link Expression} objects + */ + @NonNull + public static List findVariables(@NonNull String text) { return VAR_FINDER.find(text); } - public static List findProcessors(String text) { + /** + * Finds processors expressions in a given text. + * + * @param text the text to search for processor expressions + * @return a list of found processor expressions as {@link Expression} + * objects + */ + @NonNull + public static List findProcessors(@NonNull String text) { return PROC_FINDER.find(text); } } diff --git a/src/main/java/pro/verron/docxstamper/core/Matcher.java b/src/main/java/pro/verron/docxstamper/core/Matcher.java index 25b26d2a..cea24fdd 100644 --- a/src/main/java/pro/verron/docxstamper/core/Matcher.java +++ b/src/main/java/pro/verron/docxstamper/core/Matcher.java @@ -4,16 +4,23 @@ import org.springframework.lang.NonNull; /** - * Represents a Matcher that checks if an expression starts with a specified prefix and ends with a specified suffix. - * - * @author Joseph Verron - * @version ${version} - * @since 1.6.5 + * The Matcher class represents a matching criteria for expressions. + * It defines a prefix and suffix, + * and provides methods to match and strip expressions. */ public record Matcher( @NonNull String prefix, @NonNull String suffix ) { + + /** + * Checks if the given expression matches the specified criteria. + * + * @param expression the expression to be matched. + * @return {@code true} if the expression starts with the prefix + * and ends with the suffix, + * {@code false} otherwise. + */ @NonNull boolean match(@NonNull String expression) { return expression.startsWith(prefix) @@ -21,12 +28,13 @@ boolean match(@NonNull String expression) { } /** - * Strips the prefix and suffix from the given expression. - * @param expression the expression to strip. - * @return the stripped expression. + * Strips the prefix and suffix from the given expression and returns the inner part. + * + * @param expression the expression to be stripped. + * @return the inner part of the expression after stripping the prefix and suffix. */ @NonNull - public String strip(@NonNull String expression) { + String strip(@NonNull String expression) { int start = prefix.length(); int end = expression.length() - suffix.length(); return expression.substring(start, end); From 542e3615193b5e697d07add98d9d95c08e975b99 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 14 Mar 2024 01:42:10 +0100 Subject: [PATCH 006/134] Refactor and restructure ExpressionFinder class In this commit, the 'ExpressionFinder' class has been relocated to a dedicated 'expression' package. This commit also improves comment management by adopting an object-oriented approach instead of static methods, improving the code's readability. At the same time, 'NonNull' annotations are removed under the assumption that package-wide non-null policy is in place. --- .../wickedsource/docxstamper/DocxStamper.java | 5 ++-- .../docxstamper/util/CommentUtil.java | 5 ++-- .../verron/docxstamper/core/Expression.java | 6 ++--- .../verron/docxstamper/core/Expressions.java | 26 ++++++++++++++----- .../{ => expression}/ExpressionFinder.java | 6 ++--- .../core/{ => expression}/Matcher.java | 25 +++++++++--------- .../core/expression/package-info.java | 4 +++ .../verron/docxstamper/core/package-info.java | 4 +++ 8 files changed, 51 insertions(+), 30 deletions(-) rename src/main/java/pro/verron/docxstamper/core/{ => expression}/ExpressionFinder.java (88%) rename src/main/java/pro/verron/docxstamper/core/{ => expression}/Matcher.java (62%) create mode 100644 src/main/java/pro/verron/docxstamper/core/expression/package-info.java create mode 100644 src/main/java/pro/verron/docxstamper/core/package-info.java diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java index 12981a6d..689dc250 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java @@ -17,8 +17,7 @@ import pro.verron.docxstamper.OpcStamper; import pro.verron.docxstamper.StamperFactory; import pro.verron.docxstamper.api.ObjectResolver; -import pro.verron.docxstamper.core.Expression; -import pro.verron.docxstamper.core.Matcher; +import pro.verron.docxstamper.core.Expressions; import java.io.InputStream; import java.io.OutputStream; @@ -115,7 +114,7 @@ private DocxStamper( replaceUnresolvedExpressions, unresolvedExpressionsDefaultValue, leaveEmptyOnExpressionError, - new Expression(new Matcher("", ""), lineBreakPlaceholder) + Expressions.raw(lineBreakPlaceholder) ); for (var entry : configurationCommentProcessors.entrySet()) { diff --git a/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java b/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java index 68f04e97..b8544e56 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java +++ b/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java @@ -15,7 +15,7 @@ import org.wickedsource.docxstamper.util.walk.BaseDocumentWalker; import org.wickedsource.docxstamper.util.walk.DocumentWalker; import pro.verron.docxstamper.core.Expression; -import pro.verron.docxstamper.core.Matcher; +import pro.verron.docxstamper.core.Expressions; import java.math.BigInteger; import java.util.*; @@ -197,7 +197,8 @@ public static Expression getCommentString(Comment comment) { builder.append(new ParagraphWrapper(p).getText()); } } - return new Expression(new Matcher("", ""), builder.toString()); + String string = builder.toString(); + return Expressions.raw(string); } /** diff --git a/src/main/java/pro/verron/docxstamper/core/Expression.java b/src/main/java/pro/verron/docxstamper/core/Expression.java index 854a85a0..22105f4e 100644 --- a/src/main/java/pro/verron/docxstamper/core/Expression.java +++ b/src/main/java/pro/verron/docxstamper/core/Expression.java @@ -1,13 +1,13 @@ package pro.verron.docxstamper.core; -import org.springframework.lang.NonNull; +import pro.verron.docxstamper.core.expression.Matcher; /** * Represents an expression with a configured Matcher. */ public record Expression( - @NonNull Matcher matcher, - @NonNull String expression + Matcher matcher, + String expression ) { /** * Returns the inner part of the expression diff --git a/src/main/java/pro/verron/docxstamper/core/Expressions.java b/src/main/java/pro/verron/docxstamper/core/Expressions.java index 9a41635c..73d154f5 100644 --- a/src/main/java/pro/verron/docxstamper/core/Expressions.java +++ b/src/main/java/pro/verron/docxstamper/core/Expressions.java @@ -1,14 +1,18 @@ package pro.verron.docxstamper.core; -import org.springframework.lang.NonNull; import org.wickedsource.docxstamper.api.DocxStamperException; +import pro.verron.docxstamper.core.expression.ExpressionFinder; +import pro.verron.docxstamper.core.expression.Matcher; import java.util.List; import java.util.regex.Pattern; /** - * The Expressions class provides utility methods - * for finding variables and processors in a given text. + * The Expressions class provides utility methods for finding variables and processors in a given text. + * It contains multiple constant variables for different types of expressions, such as VAR_MATCHER for variable expressions and PROC_MATCHER for processor expressions. + * The findVariables() method uses VAR_FINDER to find variable expressions in a given text and returns a list of found expressions. + * The findProcessors() method uses PROC_FINDER to find processor expressions in a given text and returns a list of found expressions. + * The raw() method creates a new Expression object using the RAW_MATCHER and a specified text. */ public class Expressions { /** @@ -48,6 +52,12 @@ public class Expressions { */ private static final ExpressionFinder VAR_FINDER = new ExpressionFinder(VAR_PATTERN, VAR_MATCHER); + /** + * A Matcher matching raw expressions. + * It is typically used to wrap raw expressions that do not have a + * specific prefix or suffix. + */ + private static final Matcher RAW_MATCHER = new Matcher("", ""); private Expressions() { throw new DocxStamperException( @@ -60,8 +70,7 @@ private Expressions() { * @param text the text to search for variable expressions * @return a list of found variable expressions as {@link Expression} objects */ - @NonNull - public static List findVariables(@NonNull String text) { + public static List findVariables(String text) { return VAR_FINDER.find(text); } @@ -72,8 +81,11 @@ public static List findVariables(@NonNull String text) { * @return a list of found processor expressions as {@link Expression} * objects */ - @NonNull - public static List findProcessors(@NonNull String text) { + public static List findProcessors(String text) { return PROC_FINDER.find(text); } + + public static Expression raw(String text) { + return new Expression(RAW_MATCHER, text); + } } diff --git a/src/main/java/pro/verron/docxstamper/core/ExpressionFinder.java b/src/main/java/pro/verron/docxstamper/core/expression/ExpressionFinder.java similarity index 88% rename from src/main/java/pro/verron/docxstamper/core/ExpressionFinder.java rename to src/main/java/pro/verron/docxstamper/core/expression/ExpressionFinder.java index efaeeb79..a948eeb8 100644 --- a/src/main/java/pro/verron/docxstamper/core/ExpressionFinder.java +++ b/src/main/java/pro/verron/docxstamper/core/expression/ExpressionFinder.java @@ -1,6 +1,6 @@ -package pro.verron.docxstamper.core; +package pro.verron.docxstamper.core.expression; -import org.springframework.lang.NonNull; +import pro.verron.docxstamper.core.Expression; import java.util.ArrayList; import java.util.List; @@ -25,7 +25,7 @@ public record ExpressionFinder( * @param text the text to search for expressions * @return a list of found expressions */ - public List find(@NonNull String text) { + public List find(String text) { if (text.isEmpty()) return emptyList(); var matcher = pattern.matcher(text); diff --git a/src/main/java/pro/verron/docxstamper/core/Matcher.java b/src/main/java/pro/verron/docxstamper/core/expression/Matcher.java similarity index 62% rename from src/main/java/pro/verron/docxstamper/core/Matcher.java rename to src/main/java/pro/verron/docxstamper/core/expression/Matcher.java index cea24fdd..7fba876c 100644 --- a/src/main/java/pro/verron/docxstamper/core/Matcher.java +++ b/src/main/java/pro/verron/docxstamper/core/expression/Matcher.java @@ -1,16 +1,18 @@ -package pro.verron.docxstamper.core; +package pro.verron.docxstamper.core.expression; -import org.springframework.lang.NonNull; - /** - * The Matcher class represents a matching criteria for expressions. - * It defines a prefix and suffix, - * and provides methods to match and strip expressions. + * The Matcher class provides methods to match and strip expressions based on a specified prefix and suffix. + * The match() + * method checks if an expression starts with the prefix + * and ends with the suffix. + * The strip() + * method removes the prefix and suffix from an expression + * and returns the inner part. */ public record Matcher( - @NonNull String prefix, - @NonNull String suffix + String prefix, + String suffix ) { /** @@ -21,8 +23,8 @@ public record Matcher( * and ends with the suffix, * {@code false} otherwise. */ - @NonNull - boolean match(@NonNull String expression) { + + public boolean match(String expression) { return expression.startsWith(prefix) && expression.endsWith(suffix); } @@ -33,8 +35,7 @@ boolean match(@NonNull String expression) { * @param expression the expression to be stripped. * @return the inner part of the expression after stripping the prefix and suffix. */ - @NonNull - String strip(@NonNull String expression) { + public String strip(String expression) { int start = prefix.length(); int end = expression.length() - suffix.length(); return expression.substring(start, end); diff --git a/src/main/java/pro/verron/docxstamper/core/expression/package-info.java b/src/main/java/pro/verron/docxstamper/core/expression/package-info.java new file mode 100644 index 00000000..e26a8f26 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/core/expression/package-info.java @@ -0,0 +1,4 @@ +@NonNullApi +package pro.verron.docxstamper.core.expression; + +import org.springframework.lang.NonNullApi; diff --git a/src/main/java/pro/verron/docxstamper/core/package-info.java b/src/main/java/pro/verron/docxstamper/core/package-info.java new file mode 100644 index 00000000..c2a02d7e --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/core/package-info.java @@ -0,0 +1,4 @@ +@NonNullApi +package pro.verron.docxstamper.core; + +import org.springframework.lang.NonNullApi; From 451cda5da586368ceed8300eb96adde1321c1a2f Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 14 Mar 2024 01:48:11 +0100 Subject: [PATCH 007/134] Improve error handling in PlaceholderReplacer class This commit refactors the PlaceholderReplacer class. It simplifies error handling for unresolved expressions -- by flattening the conditional structure using else if clause -- and removing unnecessary methods related to unresolved expressions. This improves the readability and maintainability of the code. --- .../replace/PlaceholderReplacer.java | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java b/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java index 92d3e8a3..4b21972e 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java @@ -120,23 +120,28 @@ public void resolveExpressionsForParagraph( } catch (SpelEvaluationException | SpelParseException e) { if (failOnUnresolvedExpression) { String message = "Expression %s could not be resolved against context of type %s" - .formatted(expression, - context.getClass()); + .formatted(expression, context.getClass()); throw new DocxStamperException(message, e); - } else { + } else if (leaveEmptyOnExpressionError) { + log.warn( + "Expression {} could not be resolved against context root of type {}. Reason: {}. Set log level to TRACE to view Stacktrace.", + expression, + context.getClass(), + e.getMessage()); + log.trace("Reason for skipping expression:", e); + replace(paragraphWrapper, expression, ""); + } else if (replaceUnresolvedExpressions) { log.warn( "Expression {} could not be resolved against context root of type {}. Reason: {}. Set log level to TRACE to view Stacktrace.", expression, context.getClass(), e.getMessage()); log.trace("Reason for skipping expression:", e); - if (leaveEmptyOnExpressionError) { - replace(paragraphWrapper, expression, ""); - } else if (replaceUnresolvedExpressions()) { - replace(paragraphWrapper, - expression, - unresolvedExpressionsDefaultValue()); - } + replace(paragraphWrapper, + expression, + unresolvedExpressionsDefaultValue); + } else { + // DO NOTHING } } } @@ -174,14 +179,6 @@ public void replace( replacementRun)); } - private boolean replaceUnresolvedExpressions() { - return replaceUnresolvedExpressions; - } - - private String unresolvedExpressionsDefaultValue() { - return unresolvedExpressionsDefaultValue; - } - private Expression lineBreakPlaceholder() { return lineBreakPlaceholder; } From 932fb6da93a085be2f7153e86178325a11b22dc6 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 14 Mar 2024 05:34:12 +0100 Subject: [PATCH 008/134] Refactor PlaceholderReplacer and CommentProcessorRegistry Refactored PlaceholderReplacer for improved simplicity and error handling, and updated CommentProcessorRegistry to remove dependency on PlaceholderReplacer. These changes promote code readability and maintainability. --- .../wickedsource/docxstamper/DocxStamper.java | 1 - .../processor/CommentProcessorRegistry.java | 8 +- .../repeat/ParagraphRepeatProcessor.java | 413 ++++++++++-------- .../ParagraphResolverDocumentWalker.java | 58 ++- .../replace/PlaceholderReplacer.java | 66 +-- .../docxstamper/util/ParagraphWrapper.java | 1 + .../verron/docxstamper/core/Expressions.java | 5 + 7 files changed, 288 insertions(+), 264 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java index 689dc250..50843470 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java @@ -123,7 +123,6 @@ private DocxStamper( var commentProcessorRegistryInstance = new CommentProcessorRegistry( - placeholderReplacerInstance, expressionResolver, commentProcessors, failOnUnresolvedExpression diff --git a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java index d70d55bb..8c346a23 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java @@ -13,10 +13,10 @@ import org.wickedsource.docxstamper.api.UnresolvedExpressionException; import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.el.ExpressionResolver; -import org.wickedsource.docxstamper.replace.PlaceholderReplacer; import org.wickedsource.docxstamper.util.CommentUtil; import org.wickedsource.docxstamper.util.CommentWrapper; import org.wickedsource.docxstamper.util.ParagraphWrapper; +import org.wickedsource.docxstamper.util.RunUtil; import org.wickedsource.docxstamper.util.walk.BaseCoordinatesWalker; import pro.verron.docxstamper.core.Expressions; @@ -43,7 +43,6 @@ public class CommentProcessorRegistry { private final Logger logger = LoggerFactory.getLogger( CommentProcessorRegistry.class); - private final PlaceholderReplacer placeholderReplacer; private final Map, Object> commentProcessors; private final boolean failOnUnresolvedExpression; private final ExpressionResolver expressionResolver; @@ -51,18 +50,15 @@ public class CommentProcessorRegistry { /** * Creates a new CommentProcessorRegistry. * - * @param placeholderReplacer the placeholder replacer * @param expressionResolver the expression resolver * @param commentProcessors the comment processors * @param failOnUnresolvedExpression whether to fail on unresolved expressions */ public CommentProcessorRegistry( - PlaceholderReplacer placeholderReplacer, ExpressionResolver expressionResolver, Map, Object> commentProcessors, boolean failOnUnresolvedExpression ) { - this.placeholderReplacer = placeholderReplacer; this.expressionResolver = expressionResolver; this.commentProcessors = commentProcessors; this.failOnUnresolvedExpression = failOnUnresolvedExpression; @@ -172,7 +168,7 @@ private void runProcessorsOnInlineContent( try { expressionResolver.resolve(expression, expressionContext); - placeholderReplacer.replace(paragraphWrapper, expression, ""); + paragraphWrapper.replace(expression, RunUtil.create("")); logger.debug( "Processor expression '{}' has been successfully processed by a comment processor.", expression); diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java index 4680399c..637310e5 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java @@ -7,10 +7,7 @@ import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.replace.PlaceholderReplacer; -import org.wickedsource.docxstamper.util.CommentUtil; -import org.wickedsource.docxstamper.util.CommentWrapper; -import org.wickedsource.docxstamper.util.ParagraphUtil; -import org.wickedsource.docxstamper.util.SectionUtil; +import org.wickedsource.docxstamper.util.*; import java.math.BigInteger; import java.util.*; @@ -29,186 +26,230 @@ * @version ${version} * @since 1.2.2 */ -public class ParagraphRepeatProcessor extends BaseCommentProcessor implements IParagraphRepeatProcessor { - private final Supplier> nullSupplier; - private Map pToRepeat = new HashMap<>(); - - /** - * @param placeholderReplacer replaces placeholders with values - * @param nullSupplier supplies a list of paragraphs if the list of objects to repeat is null - */ - private ParagraphRepeatProcessor( - PlaceholderReplacer placeholderReplacer, - Supplier> nullSupplier - ) { - super(placeholderReplacer); - this.nullSupplier = nullSupplier; - } - - /** - *

newInstance.

- * - * @param pr replaces expressions with values - * @param nullReplacement replaces null values - * @return a new instance of ParagraphRepeatProcessor - */ - public static ICommentProcessor newInstance(PlaceholderReplacer pr, String nullReplacement) { - return new ParagraphRepeatProcessor(pr, () -> singletonList(ParagraphUtil.create(nullReplacement))); - } - - /** - *

newInstance.

- * - * @param placeholderReplacer replaces expressions with values - * @return a new instance of ParagraphRepeatProcessor - */ - public static ICommentProcessor newInstance(PlaceholderReplacer placeholderReplacer) { - return new ParagraphRepeatProcessor(placeholderReplacer, Collections::emptyList); - } - - /** - * Returns all paragraphs inside the comment of the given paragraph. - *

- * If the paragraph is not inside a comment, the given paragraph is returned. - * - * @param paragraph the paragraph to analyze - * @return all paragraphs inside the comment of the given paragraph - */ - public static Deque

getParagraphsInsideComment(P paragraph) { - BigInteger commentId = null; - boolean foundEnd = false; - - Deque

paragraphs = new ArrayDeque<>(); - paragraphs.add(paragraph); - - for (Object object : paragraph.getContent()) { - if (object instanceof CommentRangeStart crs) commentId = crs.getId(); - if (object instanceof CommentRangeEnd cre && Objects.equals(commentId, cre.getId())) foundEnd = true; - } - if (foundEnd || commentId == null) return paragraphs; - - Object parent = paragraph.getParent(); - if (parent instanceof ContentAccessor contentAccessor) { - int index = contentAccessor.getContent().indexOf(paragraph); - for (int i = index + 1; i < contentAccessor.getContent().size() && !foundEnd; i++) { - Object next = contentAccessor.getContent().get(i); - - if (next instanceof CommentRangeEnd cre && cre.getId().equals(commentId)) { - foundEnd = true; - } else { - if (next instanceof P p) { - paragraphs.add(p); - } - if (next instanceof ContentAccessor childContent) { - for (Object child : childContent.getContent()) { - if (child instanceof CommentRangeEnd cre && cre.getId().equals(commentId)) { - foundEnd = true; - break; - } - } - } - } - } - } - return paragraphs; - } - - private static void restoreFirstSectionBreakIfNeeded(Paragraphs paragraphs, Deque

paragraphsToAdd) { - if (paragraphs.firstParagraphSectionBreak != null) { - P breakP = paragraphsToAdd.getLast(); - SectionUtil.applySectionBreakToParagraph(paragraphs.firstParagraphSectionBreak, breakP); - } - } - - /** {@inheritDoc} */ - @Override - public void repeatParagraph(List objects) { - P paragraph = getParagraph(); - - Deque

paragraphs = getParagraphsInsideComment(paragraph); - - Paragraphs toRepeat = new Paragraphs(); - toRepeat.commentWrapper = getCurrentCommentWrapper(); - toRepeat.data = new ArrayDeque<>(objects); - toRepeat.paragraphs = paragraphs; - toRepeat.sectionBreakBefore = SectionUtil.getPreviousSectionBreakIfPresent(paragraph, - (ContentAccessor) paragraph.getParent()); - toRepeat.firstParagraphSectionBreak = SectionUtil.getParagraphSectionBreak(paragraph); - toRepeat.hasOddSectionBreaks = SectionUtil.isOddNumberOfSectionBreaks(new ArrayList<>(toRepeat.paragraphs)); - - if (paragraph.getPPr() != null && paragraph.getPPr().getSectPr() != null) { - // we need to clear the first paragraph's section break to be able to control how to repeat it - paragraph.getPPr().setSectPr(null); - } - - pToRepeat.put(paragraph, toRepeat); - } - - private Deque

generateParagraphsToAdd(WordprocessingMLPackage document, Paragraphs paragraphs, Deque expressionContexts) { - Deque

paragraphsToAdd = new ArrayDeque<>(); - - Object lastExpressionContext = expressionContexts.peekLast(); - P lastParagraph = paragraphs.paragraphs.peekLast(); - - for (Object expressionContext : expressionContexts) { - for (P paragraphToClone : paragraphs.paragraphs) { - P pClone = XmlUtils.deepCopy(paragraphToClone); - - if (paragraphs.sectionBreakBefore != null - && paragraphs.hasOddSectionBreaks - && expressionContext != lastExpressionContext - && paragraphToClone == lastParagraph - ) { - SectionUtil.applySectionBreakToParagraph(paragraphs.sectionBreakBefore, pClone); - } - - CommentUtil.deleteCommentFromElement(pClone.getContent(), paragraphs.commentWrapper.getComment().getId()); - placeholderReplacer.resolveExpressionsForParagraph(pClone, expressionContext, document); - paragraphsToAdd.add(pClone); - } - } - return paragraphsToAdd; - } - - /** - * {@inheritDoc} - */ - @Override - public void commitChanges(WordprocessingMLPackage document) { - for (Map.Entry entry : pToRepeat.entrySet()) { - P currentP = entry.getKey(); - ContentAccessor parent = (ContentAccessor) currentP.getParent(); - List parentContent = parent.getContent(); - int index = parentContent.indexOf(currentP); - if (index < 0) throw new DocxStamperException("Impossible"); - - Paragraphs paragraphsToRepeat = entry.getValue(); - Deque expressionContexts = Objects.requireNonNull(paragraphsToRepeat).data; - Deque

collection = expressionContexts == null - ? new ArrayDeque<>(nullSupplier.get()) - : generateParagraphsToAdd(document, paragraphsToRepeat, expressionContexts); - restoreFirstSectionBreakIfNeeded(paragraphsToRepeat, collection); - parentContent.addAll(index, collection); - parentContent.removeAll(paragraphsToRepeat.paragraphs); - } - } - - /** {@inheritDoc} */ - @Override - public void reset() { - pToRepeat = new HashMap<>(); - } - - private static class Paragraphs { - CommentWrapper commentWrapper; - Deque data; - Deque

paragraphs; - // hasOddSectionBreaks is true if the paragraphs to repeat contain an odd number of section breaks - // changing the layout, false otherwise - boolean hasOddSectionBreaks; - // section break right before the first paragraph to repeat if present, or null - SectPr sectionBreakBefore; - // section break on the first paragraph to repeat if present, or null - SectPr firstParagraphSectionBreak; - } +public class ParagraphRepeatProcessor + extends BaseCommentProcessor + implements IParagraphRepeatProcessor { + private final Supplier> nullSupplier; + private Map pToRepeat = new HashMap<>(); + + /** + * @param placeholderReplacer replaces placeholders with values + * @param nullSupplier supplies a list of paragraphs if the list of objects to repeat is null + */ + private ParagraphRepeatProcessor( + PlaceholderReplacer placeholderReplacer, + Supplier> nullSupplier + ) { + super(placeholderReplacer); + this.nullSupplier = nullSupplier; + } + + /** + *

newInstance.

+ * + * @param pr replaces expressions with values + * @param nullReplacement replaces null values + * @return a new instance of ParagraphRepeatProcessor + */ + // TODO: remove ? + public static ICommentProcessor newInstance( + PlaceholderReplacer pr, + String nullReplacement + ) { + return new ParagraphRepeatProcessor(pr, + () -> singletonList(ParagraphUtil.create( + nullReplacement))); + } + + /** + *

newInstance.

+ * + * @param placeholderReplacer replaces expressions with values + * @return a new instance of ParagraphRepeatProcessor + */ + public static ICommentProcessor newInstance(PlaceholderReplacer placeholderReplacer) { + return new ParagraphRepeatProcessor(placeholderReplacer, + Collections::emptyList); + } + + /** + * Returns all paragraphs inside the comment of the given paragraph. + *

+ * If the paragraph is not inside a comment, the given paragraph is returned. + * + * @param paragraph the paragraph to analyze + * @return all paragraphs inside the comment of the given paragraph + */ + public static Deque

getParagraphsInsideComment(P paragraph) { + BigInteger commentId = null; + boolean foundEnd = false; + + Deque

paragraphs = new ArrayDeque<>(); + paragraphs.add(paragraph); + + for (Object object : paragraph.getContent()) { + if (object instanceof CommentRangeStart crs) + commentId = crs.getId(); + if (object instanceof CommentRangeEnd cre && Objects.equals( + commentId, + cre.getId())) foundEnd = true; + } + if (foundEnd || commentId == null) return paragraphs; + + Object parent = paragraph.getParent(); + if (parent instanceof ContentAccessor contentAccessor) { + int index = contentAccessor.getContent() + .indexOf(paragraph); + for (int i = index + 1; i < contentAccessor.getContent() + .size() && !foundEnd; i++) { + Object next = contentAccessor.getContent() + .get(i); + + if (next instanceof CommentRangeEnd cre && cre.getId() + .equals(commentId)) { + foundEnd = true; + } else { + if (next instanceof P p) { + paragraphs.add(p); + } + if (next instanceof ContentAccessor childContent) { + for (Object child : childContent.getContent()) { + if (child instanceof CommentRangeEnd cre && cre.getId() + .equals(commentId)) { + foundEnd = true; + break; + } + } + } + } + } + } + return paragraphs; + } + + private static void restoreFirstSectionBreakIfNeeded( + Paragraphs paragraphs, + Deque

paragraphsToAdd + ) { + if (paragraphs.firstParagraphSectionBreak != null) { + P breakP = paragraphsToAdd.getLast(); + SectionUtil.applySectionBreakToParagraph(paragraphs.firstParagraphSectionBreak, + breakP); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void repeatParagraph(List objects) { + P paragraph = getParagraph(); + + Deque

paragraphs = getParagraphsInsideComment(paragraph); + + Paragraphs toRepeat = new Paragraphs(); + toRepeat.commentWrapper = getCurrentCommentWrapper(); + toRepeat.data = new ArrayDeque<>(objects); + toRepeat.paragraphs = paragraphs; + toRepeat.sectionBreakBefore = SectionUtil.getPreviousSectionBreakIfPresent( + paragraph, + (ContentAccessor) paragraph.getParent()); + toRepeat.firstParagraphSectionBreak = SectionUtil.getParagraphSectionBreak( + paragraph); + toRepeat.hasOddSectionBreaks = SectionUtil.isOddNumberOfSectionBreaks( + new ArrayList<>(toRepeat.paragraphs)); + + if (paragraph.getPPr() != null && paragraph.getPPr() + .getSectPr() != null) { + // we need to clear the first paragraph's section break to be able to control how to repeat it + paragraph.getPPr() + .setSectPr(null); + } + + pToRepeat.put(paragraph, toRepeat); + } + + private Deque

generateParagraphsToAdd( + WordprocessingMLPackage document, + Paragraphs paragraphs, + Deque expressionContexts + ) { + Deque

paragraphsToAdd = new ArrayDeque<>(); + + Object lastExpressionContext = expressionContexts.peekLast(); + P lastParagraph = paragraphs.paragraphs.peekLast(); + + for (Object expressionContext : expressionContexts) { + for (P paragraphToClone : paragraphs.paragraphs) { + P pClone = XmlUtils.deepCopy(paragraphToClone); + + if (paragraphs.sectionBreakBefore != null + && paragraphs.hasOddSectionBreaks + && expressionContext != lastExpressionContext + && paragraphToClone == lastParagraph + ) { + SectionUtil.applySectionBreakToParagraph(paragraphs.sectionBreakBefore, + pClone); + } + + CommentUtil.deleteCommentFromElement(pClone.getContent(), + paragraphs.commentWrapper.getComment() + .getId()); + placeholderReplacer.resolveExpressionsForParagraph( + new ParagraphWrapper(pClone), + expressionContext, + document + ); + paragraphsToAdd.add(pClone); + } + } + return paragraphsToAdd; + } + + /** + * {@inheritDoc} + */ + @Override + public void commitChanges(WordprocessingMLPackage document) { + for (Map.Entry entry : pToRepeat.entrySet()) { + P currentP = entry.getKey(); + ContentAccessor parent = (ContentAccessor) currentP.getParent(); + List parentContent = parent.getContent(); + int index = parentContent.indexOf(currentP); + if (index < 0) throw new DocxStamperException("Impossible"); + + Paragraphs paragraphsToRepeat = entry.getValue(); + Deque expressionContexts = Objects.requireNonNull( + paragraphsToRepeat).data; + Deque

collection = expressionContexts == null + ? new ArrayDeque<>(nullSupplier.get()) + : generateParagraphsToAdd(document, + paragraphsToRepeat, + expressionContexts); + restoreFirstSectionBreakIfNeeded(paragraphsToRepeat, collection); + parentContent.addAll(index, collection); + parentContent.removeAll(paragraphsToRepeat.paragraphs); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void reset() { + pToRepeat = new HashMap<>(); + } + + private static class Paragraphs { + CommentWrapper commentWrapper; + Deque data; + Deque

paragraphs; + // hasOddSectionBreaks is true if the paragraphs to repeat contain an odd number of section breaks + // changing the layout, false otherwise + boolean hasOddSectionBreaks; + // section break right before the first paragraph to repeat if present, or null + SectPr sectionBreakBefore; + // section break on the first paragraph to repeat if present, or null + SectPr firstParagraphSectionBreak; + } } diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java index 951c94fd..521856d1 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java @@ -4,6 +4,7 @@ import org.docx4j.wml.P; import org.docx4j.wml.Tr; import org.wickedsource.docxstamper.replace.PlaceholderReplacer; +import org.wickedsource.docxstamper.util.ParagraphWrapper; import org.wickedsource.docxstamper.util.walk.BaseDocumentWalker; /** @@ -15,29 +16,40 @@ * @version ${version} * @since 1.4.7 */ -public class ParagraphResolverDocumentWalker extends BaseDocumentWalker { - private final Object expressionContext; - private final WordprocessingMLPackage document; - private final PlaceholderReplacer placeholderReplacer; +public class ParagraphResolverDocumentWalker + extends BaseDocumentWalker { + private final Object expressionContext; + private final WordprocessingMLPackage document; + private final PlaceholderReplacer placeholderReplacer; - /** - *

Constructor for ParagraphResolverDocumentWalker.

- * - * @param rowClone The row to start with - * @param expressionContext The context of the expressions to resolve - * @param document The document to walk through - * @param replacer The placeholderReplacer to use for resolving - */ - public ParagraphResolverDocumentWalker(Tr rowClone, Object expressionContext, WordprocessingMLPackage document, PlaceholderReplacer replacer) { - super(rowClone); - this.expressionContext = expressionContext; - this.document = document; - this.placeholderReplacer = replacer; - } + /** + *

Constructor for ParagraphResolverDocumentWalker.

+ * + * @param rowClone The row to start with + * @param expressionContext The context of the expressions to resolve + * @param document The document to walk through + * @param replacer The placeholderReplacer to use for resolving + */ + public ParagraphResolverDocumentWalker( + Tr rowClone, + Object expressionContext, + WordprocessingMLPackage document, + PlaceholderReplacer replacer + ) { + super(rowClone); + this.expressionContext = expressionContext; + this.document = document; + this.placeholderReplacer = replacer; + } - /** {@inheritDoc} */ - @Override - protected void onParagraph(P paragraph) { - placeholderReplacer.resolveExpressionsForParagraph(paragraph, expressionContext, document); - } + /** + * {@inheritDoc} + */ + @Override + protected void onParagraph(P paragraph) { + placeholderReplacer.resolveExpressionsForParagraph( + new ParagraphWrapper(paragraph), + expressionContext, document + ); + } } diff --git a/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java b/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java index 4b21972e..ffe5cb2e 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java @@ -18,8 +18,6 @@ import pro.verron.docxstamper.core.Expression; import pro.verron.docxstamper.core.Expressions; -import java.util.Optional; - /** * Replaces expressions in a document with the values provided by the {@link ExpressionResolver}. * @@ -89,9 +87,10 @@ public void resolveExpressions( new BaseCoordinatesWalker() { @Override protected void onParagraph(P paragraph) { - resolveExpressionsForParagraph(paragraph, - expressionContext, - document); + resolveExpressionsForParagraph( + new ParagraphWrapper(paragraph), + expressionContext, + document); } }.walk(document); } @@ -99,24 +98,22 @@ protected void onParagraph(P paragraph) { /** * Finds expressions in the given paragraph and replaces them with the values provided by the expression resolver. * - * @param p the paragraph in which to replace expressions. - * @param context the context root - * @param document the document in which to replace all expressions. + * @param paragraph the paragraph in which to replace expressions. + * @param context the context root + * @param document the document in which to replace all expressions. */ public void resolveExpressionsForParagraph( - P p, + ParagraphWrapper paragraph, Object context, WordprocessingMLPackage document ) { - var paragraphWrapper = new ParagraphWrapper(p); - String text = paragraphWrapper.getText(); - var expressions = Expressions.findVariables(text); + var expressions = Expressions.findVariables(paragraph); for (var expression : expressions) { try { var resolution = resolver.resolve(expression, context); var replacement = registry.resolve(document, expression, resolution); - replace(paragraphWrapper, expression, replacement); + paragraph.replace(expression, replacement); } catch (SpelEvaluationException | SpelParseException e) { if (failOnUnresolvedExpression) { String message = "Expression %s could not be resolved against context of type %s" @@ -129,7 +126,7 @@ public void resolveExpressionsForParagraph( context.getClass(), e.getMessage()); log.trace("Reason for skipping expression:", e); - replace(paragraphWrapper, expression, ""); + paragraph.replace(expression, RunUtil.create("")); } else if (replaceUnresolvedExpressions) { log.warn( "Expression {} could not be resolved against context root of type {}. Reason: {}. Set log level to TRACE to view Stacktrace.", @@ -137,59 +134,32 @@ public void resolveExpressionsForParagraph( context.getClass(), e.getMessage()); log.trace("Reason for skipping expression:", e); - replace(paragraphWrapper, + paragraph.replace( expression, - unresolvedExpressionsDefaultValue); + RunUtil.create(unresolvedExpressionsDefaultValue)); } else { // DO NOTHING } } } if (lineBreakPlaceholder() != null) { - replaceLineBreaks(paragraphWrapper); + replaceLineBreaks(paragraph); } } - private void replace( - ParagraphWrapper p, - Expression expression, - R replacementRun - ) { - p.replace(expression, - replacementRun == null ? RunUtil.create("") : replacementRun); - } - - /** - * Replaces expressions in the given paragraph and replaces them with the values provided by the expression resolver. - * - * @param p the paragraph in which to replace expressions. - * @param expression the expression to replace. - * @param replacementObject the object to replace the expression with. - */ - public void replace( - ParagraphWrapper p, - Expression expression, - String replacementObject - ) { - Optional.ofNullable(replacementObject) - .map(replacementStr -> RunUtil.create(replacementStr, - p.getParagraph())) - .ifPresent(replacementRun -> replace(p, - expression, - replacementRun)); - } + // TODO: Remove this intermediate method private Expression lineBreakPlaceholder() { return lineBreakPlaceholder; } - private void replaceLineBreaks(ParagraphWrapper paragraphWrapper) { + private void replaceLineBreaks(ParagraphWrapper paragraph) { Br lineBreak = Context.getWmlObjectFactory() .createBr(); R run = RunUtil.create(lineBreak); - while (paragraphWrapper.getText() + while (paragraph.getText() .contains(lineBreakPlaceholder().inner())) { - replace(paragraphWrapper, lineBreakPlaceholder(), run); + paragraph.replace(lineBreakPlaceholder(), run); } } } diff --git a/src/main/java/org/wickedsource/docxstamper/util/ParagraphWrapper.java b/src/main/java/org/wickedsource/docxstamper/util/ParagraphWrapper.java index 427db00a..190ecc67 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/ParagraphWrapper.java +++ b/src/main/java/org/wickedsource/docxstamper/util/ParagraphWrapper.java @@ -24,6 +24,7 @@ * @version ${version} * @since 1.0.8 */ +// TODO: Rename into Paragraph public class ParagraphWrapper { private final List runs = new ArrayList<>(); private final P paragraph; diff --git a/src/main/java/pro/verron/docxstamper/core/Expressions.java b/src/main/java/pro/verron/docxstamper/core/Expressions.java index 73d154f5..9d55c188 100644 --- a/src/main/java/pro/verron/docxstamper/core/Expressions.java +++ b/src/main/java/pro/verron/docxstamper/core/Expressions.java @@ -1,6 +1,7 @@ package pro.verron.docxstamper.core; import org.wickedsource.docxstamper.api.DocxStamperException; +import org.wickedsource.docxstamper.util.ParagraphWrapper; import pro.verron.docxstamper.core.expression.ExpressionFinder; import pro.verron.docxstamper.core.expression.Matcher; @@ -74,6 +75,10 @@ public static List findVariables(String text) { return VAR_FINDER.find(text); } + public static List findVariables(ParagraphWrapper paragraph) { + return findVariables(paragraph.getText()); + } + /** * Finds processors expressions in a given text. * From 33fcdcbd4f16bf124e2834a8ad0bd54888e0b7f9 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 14 Mar 2024 13:59:40 +0100 Subject: [PATCH 009/134] Refactor "TableResolver" code and expand "StampTable" functionality The "TableResolver" code has been cleaned up and refactored for better readability and maintenance. The "StampTable" class has been extended from "AbstractSequentialList" to add list-related functions. A new test, "StampTableTest" was created to verify the improvements and ensure the correct function of both classes. --- .../processor/table/StampTable.java | 30 ++- .../processor/table/TableResolver.java | 255 +++++++++--------- .../verron/docxstamper/StampTableTest.java | 61 +++++ .../docxstamper/utils/context/Contexts.java | 27 +- test/sources/StampTableTest.docx | Bin 0 -> 18582 bytes 5 files changed, 236 insertions(+), 137 deletions(-) create mode 100644 src/test/java/pro/verron/docxstamper/StampTableTest.java create mode 100644 test/sources/StampTableTest.docx diff --git a/src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java b/src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java index 9ba932a9..0a041310 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java @@ -1,7 +1,11 @@ package org.wickedsource.docxstamper.processor.table; +import org.springframework.lang.NonNull; + +import java.util.AbstractSequentialList; import java.util.ArrayList; import java.util.List; +import java.util.ListIterator; import static java.util.Collections.singletonList; @@ -12,7 +16,8 @@ * @version ${version} * @since 1.6.2 */ -public class StampTable { +public class StampTable + extends AbstractSequentialList> { private final List headers; private final List> records; @@ -31,13 +36,24 @@ public StampTable() { * @param records the lines that the table should contain */ public StampTable( - List headers, - List> records + @NonNull List headers, + @NonNull List> records ) { this.headers = headers; this.records = records; } + @Override + public int size() { + return records.size(); + } + + @Override + @NonNull + public ListIterator> listIterator(int index) { + return records.listIterator(index); + } + /** *

empty.

* @@ -59,12 +75,4 @@ public List headers() { return headers; } - /** - *

records.

- * - * @return a {@link List} object - */ - public List> records() { - return records; - } } diff --git a/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java b/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java index 59150be3..4cedeead 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java @@ -23,125 +23,140 @@ * @version ${version} * @since 1.6.2 */ -public class TableResolver extends BaseCommentProcessor implements ITableResolver { - private final Map cols = new HashMap<>(); - private final Function> nullSupplier; - - private TableResolver(PlaceholderReplacer placeholderReplacer, - Function> nullSupplier) { - super(placeholderReplacer); - this.nullSupplier = nullSupplier; - } - - /** - * Generate a new {@link TableResolver} instance - * - * @param pr a {@link PlaceholderReplacer} instance - * @param nullReplacementValue in case the value to interpret is null - * @return a new {@link TableResolver} instance - */ - public static ICommentProcessor newInstance(PlaceholderReplacer pr, String nullReplacementValue) { - return new TableResolver(pr, table -> List.of(ParagraphUtil.create(nullReplacementValue))); - } - - /** - * Generate a new {@link TableResolver} instance where value is replaced by an empty list when null - * - * @param pr a {@link PlaceholderReplacer} instance - * @return a new {@link TableResolver} instance - */ - public static ICommentProcessor newInstance(PlaceholderReplacer pr) { +public class TableResolver + extends BaseCommentProcessor + implements ITableResolver { + private final Map cols = new HashMap<>(); + private final Function> nullSupplier; + + private TableResolver( + PlaceholderReplacer placeholderReplacer, + Function> nullSupplier + ) { + super(placeholderReplacer); + this.nullSupplier = nullSupplier; + } + + /** + * Generate a new {@link TableResolver} instance + * + * @param pr a {@link PlaceholderReplacer} instance + * @param nullReplacementValue in case the value to interpret is null + * @return a new {@link TableResolver} instance + */ + public static ICommentProcessor newInstance( + PlaceholderReplacer pr, + String nullReplacementValue + ) { + return new TableResolver(pr, + table -> List.of(ParagraphUtil.create( + nullReplacementValue))); + } + + /** + * Generate a new {@link TableResolver} instance where value is replaced by an empty list when null + * + * @param pr a {@link PlaceholderReplacer} instance + * @return a new {@link TableResolver} instance + */ + public static ICommentProcessor newInstance(PlaceholderReplacer pr) { return new TableResolver(pr, table -> Collections.emptyList()); - } - - /** {@inheritDoc} */ - @Override - public void resolveTable(StampTable givenTable) { - P p = getParagraph(); - if (p.getParent() instanceof Tc tc && tc.getParent() instanceof Tr tr) { - Tbl table = (Tbl) tr.getParent(); - cols.put(table, givenTable); - } - throw new CommentProcessingException("Paragraph is not within a table!", p); - } - - /** {@inheritDoc} */ - @Override - public void commitChanges(WordprocessingMLPackage document) { - for (Map.Entry entry : cols.entrySet()) { - Tbl wordTable = entry.getKey(); - - StampTable stampedTable = entry.getValue(); - - if (stampedTable != null) { - replaceTableInplace(wordTable, stampedTable); - } else { - List tableParentContent = ((ContentAccessor) wordTable.getParent()).getContent(); - int tablePosition = tableParentContent.indexOf(wordTable); - List toInsert = nullSupplier.apply(wordTable); - tableParentContent.set(tablePosition, toInsert); - } - } - } - - private void replaceTableInplace(Tbl wordTable, StampTable stampedTable) { - List stampedHeaders = stampedTable.headers(); - List> stampedRecords = stampedTable.records(); - - List rows = wordTable.getContent(); - Tr headerRow = (Tr) rows.get(0); - Tr firstDataRow = (Tr) rows.get(1); - - growAndFillRow(headerRow, stampedHeaders); - - if (!stampedRecords.isEmpty()) { - growAndFillRow(firstDataRow, stampedRecords.get(0)); - - for (List rowContent : stampedRecords.subList(1, stampedRecords.size())) { - rows.add(copyRowFromTemplate(firstDataRow, rowContent)); - } - } else { - rows.remove(firstDataRow); - } - } - - private void growAndFillRow(Tr row, List values) { - List cellRowContent = row.getContent(); - - //Replace text in first cell - JAXBElement cell0 = (JAXBElement) cellRowContent.get(0); - Tc cell0tc = cell0.getValue(); - setCellText(cell0tc, values.isEmpty() ? "" : values.get(0)); - - if (values.size() > 1) { - //Copy first cell and replace content for each remaining values - for (String cellContent : values.subList(1, values.size())) { - JAXBElement xmlCell = XmlUtils.deepCopy(cell0); - setCellText(xmlCell.getValue(), cellContent); - cellRowContent.add(xmlCell); - } - } - } - - private Tr copyRowFromTemplate(Tr firstDataRow, List rowContent) { - Tr newXmlRow = XmlUtils.deepCopy(firstDataRow); - List xmlRow = newXmlRow.getContent(); - for (int i = 0; i < rowContent.size(); i++) { - String cellContent = rowContent.get(i); - Tc xmlCell = ((JAXBElement) xmlRow.get(i)).getValue(); - setCellText(xmlCell, cellContent); - } - return newXmlRow; - } - - private void setCellText(Tc tableCell, String content) { - tableCell.getContent().clear(); - tableCell.getContent().add(ParagraphUtil.create(content)); - } - - /** {@inheritDoc} */ - @Override - public void reset() { - cols.clear(); - } + } + + /** + * {@inheritDoc} + */ + @Override + public void resolveTable(StampTable givenTable) { + P p = getParagraph(); + if (p.getParent() instanceof Tc tc + && tc.getParent() instanceof Tr tr + && tr.getParent() instanceof Tbl table + ) cols.put(table, givenTable); + else throw new CommentProcessingException("Paragraph is not within a " + + "table!", p); + } + + /** + * {@inheritDoc} + */ + @Override + public void commitChanges(WordprocessingMLPackage document) { + for (Map.Entry entry : cols.entrySet()) { + Tbl wordTable = entry.getKey(); + + StampTable stampedTable = entry.getValue(); + + if (stampedTable != null) { + replaceTableInplace(wordTable, stampedTable); + } else { + List tableParentContent = ((ContentAccessor) wordTable.getParent()).getContent(); + int tablePosition = tableParentContent.indexOf(wordTable); + List toInsert = nullSupplier.apply(wordTable); + tableParentContent.set(tablePosition, toInsert); + } + } + } + + private void replaceTableInplace(Tbl wordTable, StampTable stampedTable) { + var headers = stampedTable.headers(); + + var rows = wordTable.getContent(); + var headerRow = (Tr) rows.get(0); + var firstDataRow = (Tr) rows.get(1); + + growAndFillRow(headerRow, headers); + + if (stampedTable.isEmpty()) + rows.remove(firstDataRow); + else { + growAndFillRow(firstDataRow, stampedTable.get(0)); + for (var rowContent : stampedTable.subList(1, stampedTable.size())) + rows.add(copyRowFromTemplate(firstDataRow, rowContent)); + } + } + + private void growAndFillRow(Tr row, List values) { + List cellRowContent = row.getContent(); + + //Replace text in first cell + JAXBElement cell0 = (JAXBElement) cellRowContent.get(0); + Tc cell0tc = cell0.getValue(); + setCellText(cell0tc, values.isEmpty() ? "" : values.get(0)); + + if (values.size() > 1) { + //Copy first cell and replace content for each remaining values + for (String cellContent : values.subList(1, values.size())) { + JAXBElement xmlCell = XmlUtils.deepCopy(cell0); + setCellText(xmlCell.getValue(), cellContent); + cellRowContent.add(xmlCell); + } + } + } + + private Tr copyRowFromTemplate(Tr firstDataRow, List rowContent) { + Tr newXmlRow = XmlUtils.deepCopy(firstDataRow); + List xmlRow = newXmlRow.getContent(); + for (int i = 0; i < rowContent.size(); i++) { + String cellContent = rowContent.get(i); + Tc xmlCell = ((JAXBElement) xmlRow.get(i)).getValue(); + setCellText(xmlCell, cellContent); + } + return newXmlRow; + } + + private void setCellText(Tc tableCell, String content) { + tableCell.getContent() + .clear(); + tableCell.getContent() + .add(ParagraphUtil.create(content)); + } + + /** + * {@inheritDoc} + */ + @Override + public void reset() { + cols.clear(); + } } diff --git a/src/test/java/pro/verron/docxstamper/StampTableTest.java b/src/test/java/pro/verron/docxstamper/StampTableTest.java new file mode 100644 index 00000000..b21a353f --- /dev/null +++ b/src/test/java/pro/verron/docxstamper/StampTableTest.java @@ -0,0 +1,61 @@ +package pro.verron.docxstamper; + +import org.junit.jupiter.api.Test; +import org.wickedsource.docxstamper.DocxStamperConfiguration; +import pro.verron.docxstamper.utils.TestDocxStamper; +import pro.verron.docxstamper.utils.context.Contexts; + +import java.nio.file.Path; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.wickedsource.docxstamper.DefaultTests.getResource; + +/** + * A test class that verifies that stampTable feature works correctly + */ +class StampTableTest { + + @Test + void stampTableTest() { + + var testDocx = getResource(Path.of("StampTableTest.docx")); + + var stamper = new TestDocxStamper<>(new DocxStamperConfiguration()); + + String string = stamper.stampAndLoadAndExtract( + testDocx, + Contexts.characterTable( + List.of("Character", "Actor"), + List.of( + List.of("Homer Simpson", "Dan Castellaneta"), + List.of("Marge Simpson", "Julie Kavner"), + List.of("Bart Simpson", "Nancy Cartwright"), + List.of("Kent Brockman", "Harry Shearer"), + List.of("Disco Stu", "Hank Azaria"), + List.of("Krusty the Clown", "Dan Castellaneta") + ) + ) + ); + assertEquals(""" + Stamping Table + List of Simpsons characters + Character + Actor + Homer Simpson + Dan Castellaneta + Marge Simpson + Julie Kavner + Bart Simpson + Nancy Cartwright + Kent Brockman + Harry Shearer + Disco Stu + Hank Azaria + Krusty the Clown + Dan Castellaneta + + There are 6 characters in the above table.""", + string); + } +} diff --git a/src/test/java/pro/verron/docxstamper/utils/context/Contexts.java b/src/test/java/pro/verron/docxstamper/utils/context/Contexts.java index 569854f3..f3f3842f 100644 --- a/src/test/java/pro/verron/docxstamper/utils/context/Contexts.java +++ b/src/test/java/pro/verron/docxstamper/utils/context/Contexts.java @@ -1,5 +1,6 @@ package pro.verron.docxstamper.utils.context; +import org.wickedsource.docxstamper.processor.table.StampTable; import org.wickedsource.docxstamper.replace.typeresolver.image.Image; import java.util.*; @@ -7,9 +8,9 @@ /** *

Contexts class.

* - * @since 1.6.5 * @author Joseph Verron * @version ${version} + * @since 1.6.5 */ public class Contexts { private Contexts() { @@ -51,7 +52,9 @@ record Name(String name) {} public static Object names(String... names) { record Name(String name) {} record Names(List names) {} - return new Names(Arrays.stream(names).map(Name::new).toList()); + return new Names(Arrays.stream(names) + .map(Name::new) + .toList()); } /** @@ -221,6 +224,16 @@ public static NullishContext nullishContext() { ); } + public static TableContext characterTable( + List headers, + List> records + ) { + return new TableContext(new StampTable( + headers, + records + )); + } + /** * The Role class represents a role played by an actor. */ @@ -521,14 +534,14 @@ public String toString() { * Represents the context of a school. * * @param schoolName the name of the school - * @param grades the list of grades in the school + * @param grades the list of grades in the school */ public record SchoolContext(String schoolName, List grades) {} /** * Represents a grade in a school. * - * @param number the grade number + * @param number the grade number * @param classes the list of classes in the grade */ record Grade(int number, List classes) {} @@ -559,8 +572,8 @@ public record AClass(int number, List students) {} * Represents a student. * * @param number the student number - * @param name the student name - * @param age the student age + * @param name the student name + * @param age the student age */ record Student(int number, String name, int age) {} @@ -594,4 +607,6 @@ public record Context(CustomType name) {} * It is used in various contexts within the application. */ public static class CustomType {} + + public record TableContext(StampTable characters) {} } diff --git a/test/sources/StampTableTest.docx b/test/sources/StampTableTest.docx new file mode 100644 index 0000000000000000000000000000000000000000..a5fb4f5e8ed6eb5f6788a1db4907b8e73495aa83 GIT binary patch literal 18582 zcmeIaV~{1=wgy_Z?Jld!wyiGPw%KK?%XU?lZQHi(?y}8Sz0cWq@6%_$c=3MUyCP;r zf z-1&{fkTICu@yyY2)F2F)I@;fiHNXk@>mk|I`VP*>VLbp-1IIY%R4K|9sdi>2iw$1l z#(lNn!C2!pkh}Ys67dYVzWVr4D@oz8KD3ZtW&??z9S<;kCL3cXVGma={7mGPmnSU{ zG*UuFl?Sb|#CNa}P-D-QJ>9-z+J+J>l@diOWq_`wL0eSiQ7o5&+qm3o7A|1FbSTOC zlR6l|_J_JrYnbs&-3vIoz#1jQ`xm)q=Rv`5fS&Kv9UL#HMB8svig|ieob#D zY8o~>A}C&!bMLxpo~ECWRx$kfAwTyRvC32CL^n+ObL(1IMR78&H5_@Lr(8Y&k%s!7 zm8hdqEIF`m;&}fwx4+jz7hM5UK#SeeZnT!x>J1frp;KEw?rfK`(bO`)A>j!V^E2(~lggGiQ4Y6w)l(DTq9%fTg=hkpnFHOFP47cBtmXMW(~g zk7)whMH`cQ^ZC(Ta4x|`O;0JeS?(MVhb=~L|ZY$R1mX-oP9~(yY-{} zEjI{tkwqUntjw8V>KEl#*%$>OmxMfxTG>XJys@VluvHcKP>`e**|-=NiNWA$;5$N6 zp_-L@_u_!-%mHxWS*g(2gG%XOD!*C#BD27h;*iybbwNt%mj|(5sHQ*xTsMD851bis zV-BF&A?GWzb@UCHR2ZSyy0Z(+b#&6g_sBEMNHpDcGL15lSPT2H2SbG_JCAR&NWlm% z3zfof_Y_qcAX^v0M+5;6%cU8IhOoIszC&npDKi1BVL>=}0DMq_*0&jM0cCOO6-NFQ zq_9q^&?NOVhsGp_)Q;XlhD@Q+LNL7HR1o|pZ*TnJ9f!;!O@XnZl#FHRe&X|hYv=-E zW#t&9f_yk8DtZZxLaZ&AhCdyk-ZzX-H3)|TTNAQr-{eI+F1yon*+9ScK~3Hb&&NH9 zBkD!-NXCG_d&E*~ml@ciFw`WH zw89&p@TV*aEdX)DQNN2byb#W#!DLw*d*lK7d2E9aZzE2m4z0S@f%z3LexRPJDpFPk z>IE<@hi6E1{~6`PXCWPB^hBW>?q^d+7Ks3U>)cHcwBb(u^`XZ|C(?=PcWC<>-+;Q$>@5 z1_v6Cih{W|+VMrfAeDqYcuw&$t##Tv3#&UYjP|fr9Z&y_W)hBFE!YjVCgGY6C)`oV zPGYc*bA7sxqV363^xc^RprqiSElaMZM;4qMoPAoKg782bw6^XZlIYWH?svKVG~&0< zra%@a$#Iz^)%N}HFJ~%3@0tdmJ`179r{_>XO)BojxGvd~dJoBCw)LTqW%|(66&Dzq z$)X~u>=C=qVMdbuc$j)Y|CvX%SF^=Qdr>ha&S3pMDZVKxW6pV2cMKP`I&;@_oa(%+ z+J1hpSw+oV%%A9pmwnGOt%xgPyP!AM>~rDR0-sws<$`DZzSzXJ&ZE(LSY^;NI&?qH z(+Lut!3^Tf`|6}9y)_>}MW!fbveEIKJkjXr^-7UtyOHX}{z3f;iUFnU-p>1N|6Ibq zGB9)iSk{}LMRoVPLnrrk)+$dgUKVQ8`Lak!Q>ync;YQ8ZoksC&tTRrUHfag!S=2eP zO8{b2cgt-APpk?cZ8-D*2c)E32-?r3zEs<8HG~1^{`0A&zFZ?jF?97`_m3Oq>K+Kp zMM6!nN>G={GEL9>?ezRi2=4hVl{J8%kA7f|Ne`NjvSJYxif$NJE{Z&EhIjLRxZ(eH z)Lk5_y7C_`y!pdf;{ZSa{^6+qrDp!_uK!dvfFF+}AHDx~f6C&!<$LH5g3c!%;4;>% z)D=O3dxSH#OCA6SmJ=hbVuvfBY-~WL@uOp^2iL9M-jB~nbBAlJVp(lfv9+qW##MpT zv6PNWbSG;tL$7FTC` z<}+2G?g^^ceclT%^0}`mo75`m9^gzJRzw|>PPor1r+N`yjW?p+o)}t0W5TW4%AaOJ zz6NV6vPG5c?ocRJF?gZtGfF}@r}G9P+|+yNhKQw)P9ws%qR!w~kf)S>?%!&^2Al-z zJb4ArGIZ@Eo>Dz$GU!C}KXh6~U9%WwK^7e16wX!xCg|dbT)zK;BbsH>OSGF@(#0+| za8miDZqlGMn-5nFL??6t|JWX4ZxRf<8IEr94htRc2cIq8+RmY7ocnEGK z6pj%HnI{f`0q@Qjx~kgeY}_jSyy-AuqQ;UK#KTr``n>*l=5e?D7)dsfNI9OD-1^Yx zfJ-TWNnf4W%KNDjTw!rKC?tefeyX{(aD`jPSha_QxP%8<|np3WDm?6HUFA>Us z5N3-7#)Ih)#`shn?3KHZZZMB^Zjfj$>5%$7P{dc?t`an}wWyn@KVlAo(xZiH!x|fgX>=B&IXc)J16oEsUWo31vq+tpXkty%FV9&5P0B zAs^(RaBJ%-;M!mnc4k@wb;bXdm-2Iw#5HXu}5lUdSXzQRzPfg zXJ-#m$|V~dtm!?L9GsjdOW}sGRu-tZ#|{|2<;oVlErSQRD)4>j%SL{y$J)S3HS>TL zQza!Cv?W820~PepKp)c4&+<8@hzzolcob)#9T*P$hg@G)oULi1J76wIuJfb}BR@P*f~rf$dX6PsjR3$G^qm&vN0btSjnny6UBlE2f`%$BI;SLAz28S{^UR8dU-5Mb0_ z%(h1?i4j(~+uZU-xOEpKQh&PS8-IV?GAGCzu1s@pp)^r{YVjEpkfV;H_N)uN=RL!#)4u#~n?Ltc+;?*wX(? zbW4U~iz0M@UwsN{9{oCgP_1f_7qhomVbxaSAJr~zQBa;~Hod-5PZy+{We*9G9xVvd zOoAW)L*KU-E9f=iC%y9$qIfTsYF3QexuDxqp*tT{S<+ciQb9AG-f2r)FoF2PuPSdp zaWX*@oJ^2~P<+YW2KFg1=olh27V$e(H?>P-pu67v?3amLT+D3@PW-G8+&Oz9ibBr* z?-)IFNI#cQ$mm#&xkQj!xTqr8IKbNJsg))2)#*iu?J_|6vz-xMABo6YdGO*0oL!Pu z+)>_0_Q54swL|u*a>dm=Lz^MUT_O>znyaQlaN#ew4>?dEA(5#Y^WN8X&K`Fp zLPn@-*qb}vG=``_MB>5{cel4Crmj6I7qkx-0VrAR+P_vVl$PI^0bs%D6;hxpn6x9g zLR-ScvrxCR-dfjNZaF-aVm~xg@_=dk&lEKJp5ObA1+mMn+^}AF6)qp9J~5M9EwEhN z%C{cR!?uA5#}miFIu2JV`R`k+MAPRcm0c8^-ySD`atWe3Oec(x*=v;bAJFW20+{|J z6h{)BD0KZ64%i;)+-uwLT09^(^zv z6(A|}enX#@`o1)R(a#jMCdg0|!;$8VLjCjWj1hj=gAQz zO`K$?FL*{Ua5Y4e27(w?s60U&dbKpF4@3}_jxD7wPInzp#Pa}1qOtCorrWz-$-CN! z54#L3G(OgJDyD0kImnJnWT~+6qdG=^HC;6~mk~N*YNI##y8%XfoY)>YeMCF!n*5RVsOY4q_}Y>YER=$oSl2WTg2n#%tWm6!{ww>u!UN`rf*zshM5FOp$caTQlW55pXv0#TYN|h3| zxIF>fkZ3;(eBwWj^@;VwMr!VtLQ;YbeX_q`Ccc@F2^5~~`JOXpW0cd0rtLdA%zu>9 zbe{hWe+@rRbmU9%%S>zG*`d_{Q=?Y3Wo28A-L%iqun5OxZ zLcvm=oQ(&BPAPFsBDSgiUAh6AgVLutnCZIx$ZaxYrd^5yWJ(1BEK+J0a`Ygtq%nql z0q2+y`Q%KA`%lQB&4S`E)tN59(hS!75?ZI1L?nw;o*s#_I&NgUml*K-i7{o+4#h~v zC+>bq!Q(zR{7f9qaEISEg>=a~wUxne5D8Qka1b-0&&I+m4$bXQBJQb|5rrMGa{41; zzx<%kY*cZyf1Z+BbTPU^Ph*Ekh}(Ex@NADx9U>X0Mh|IWK7HKn)w*I=R@-1z%`bN6 zUSv*cwqB*ad_xzARBuS;m!@x!9Tkx_FH}X?OVkMb(ZIP=XXAfcq=epAQ!&>bfxdcj zK_ldaUNVoFAfOPaLyZGEsuF8PI+Z(mV*iDMQAYjjHFopPFYFVF1Z=0uMTvIt)Wzz& z!0LVJex?qM6K4XrE(&J95wBJ&Jn!A1$K{T8lucV(R8sz3GeLN`)MB?Vb(zja(&vHE z=Q7Fxq`1v6r zbw4J{|A{|27&$tcS)2SzLMpFDuCgF_(oT8P-Ds8@s>NC{Z*(0?*7jQ?^=Y6SV3SOv zRkuM9drD4wpGPB6pMwTwbS%xKr}S>y{t)}bgNJc?TBRs-5xUq3*^c)kkQ{PTs*N`@ z@ongsBEmJaXv=j6xB7}_Npx#kElm3ns$)q||R*V1?$-+v2p?*6lsP(&MK%3zv zsUb}mb+6_sE^sKt9)44aSUC>%vm6V+XVIGHO2HEhC$DEO*KWIefyBZP$r(n#dK6G) zD4GztB>e65flKV~vm2~cF785-pxdug6%&kKDu_Zh5b{8IGzaO;*?~g1eiH0EaUF&R zI|iN>@+OX9`cgWnY4syDw}6qNnJE%D9TE11R7z5^Pvfeo5DC*Brhb0JW*$(mP1qa(+s(>nkK^SMJg zP7uK&uZa+X3DDa?MIqW?i+7D^P~^?=$QOo{;RGqf2-*d!h;#~U#@+1XOUA(y757I+ zjV+o|tuR;YLKp*Cl$ZUf5;iF?$rnI8CWfs?GF;XGMT-u9K;G~KqsD9x4ueBq)C&#i zjzV&P7$9N)7?=W(Ce6$7W&2hndLnBJwvp4-kCA&5vK;hz4t2}h(((>=ViJzlPrkXI zm7vZ6ws)FMI>?_?!C%{NEY6m`p|wo}&EJa&t3CW%Z|n9Aw_ysYS)HkaHKS1q;zs36 zd7|w!_kz$E!YB!UyC~7P@8!_`HSRXtB>fF9kErtMns3J@Kp25UlB_HE5}dJ`xFYwU zMQcHnm^H`7f?$V5;K18!pHTtO=w{y10&GiCQO8e`Gq~xfU6xqeSH9%hQmYPoIULeE zn3b!mQJEDEr~PFUtQDZNCocR|?FAR=*L~Jm1PlspB@pSh^90`KHD}_4&$H4Gi1SXf z@s9<(m*mTeEjB$&TnQKQjqs18=G8;`a{kNUipjlxF0BWaF|`?wu<2G)%lanaoEkfp z;wF+A)XXfhP*fD#lXRb~%}y-%iBzq<@6#uHIJK({rdH98Ar}>}RyS&JfFl z0x=i!L)J&bJKqB*Aq^004yp008iRiev{zH%p^Ge8P#= zxcw>{Qisia2e4$}y5K;=OWK@kf#ot@D~k+QHKRf+Ygho)HwuB?EVhZ-6T2)u9bIip zK!kWcX8s)hp*TYW6%w@JHvq@`#dQFRi|0?g6R}&s!}cRHyWJBmtNuQDh~Z{my}4Yy zT`qS_5%z?W2lRU6?YUeDAGZcSy*(wJf703SR}i@J0nU3YRdeJ@M(cl>Ur@%A9;O=W zG;oN$ibGBu@A2~t zese^u2T?8c%OO|gG@jKIo*LRDBU%jCPyb93)D5b~t2mm<3E5u;CJi3OayhuR&v#^m$~fZ;z?H{5vSGBx$AE4d2eHUltpl zWq83u_q_@$rV(Zo#Gro??R>WeLv9jNqAY`90>!)g z{RJNu`S9*_6k>|xD|d%PFQ&~?1cF%KJc$+yO}}?#w{!f&%3dKciZ8>GT440LC_hY~ zP(?%oZmlL}Ya7)3)R_Zw-MuF4DX)qk_~iW5wga={jV7#kvNVg(Z1SL?8@DA`c116J z6=H+|J~q~17YS3OFQq9CF!B@|n5gJ3#tTM-fvp0FOgCX1y7@$h#l@ZV+AVtztc$0F zZLd}q;F|3sxCiUn?dAH%S8Oi5TA$k2KfbcyvQ{z&RY^BCn@%%A9tev^B_ zsV*5=fzA~BN_@>A22ZDCs1Vx3s*W-;dCV;oJI9@cu8)LPB~F)fZzOWHflGD>9(!@< zjW9qzcny&p^L-2k%kJ{5l^B-0vQESS2{{!G z2UU16KIHAzTrJ&*>U%}?l!k|^^z~Z{5`MQ?3UMFu0yZxVY4y`2EV0ykv2Va3FJm;_ zD;ZeoxJR*Aj@wMUz;zy7;16+|1n;Mrw2vLuUpr9cof~JSF+&KID!80?7B0B~p|@`v zuTA;)W<$_EZ-*k~DPXL6Xd5Qm9#|ouyUwbfV@u95YA+~jk3(kA#5zvxjxmUzn59SG zpscbV2^WDTk=xN+Ga(wfj=xk}Xp@Emoz6^p{Aj>IFV9O5Q_gFLjjN$FeXgaZdU9Wi z1wdySZJ*A@UGN9*MBFc z!anpPfpjOX=IvW0G)SmCjQ=DNrz-d6!IBlkp5TxV7T{S@nS}yT(d%{&xK2zU{knAe z(odBqzE$bh)Yj^SyH?$tYz)=^0LWz|+-j<@N}NC3FPI~2cs zc-H?y2j1VR>-Li+@Wi67sCe?vIala;%*jPt3$D%GQ{G1fS1P^bJgFCoDzKY^Zmg*^ zC^h+snYEuGB$`JsG)Pe0&&ZXexB+DG7EH$s}6lw0Fx{YZsO((V?OFLsMhL!WVos{ zJSn#T8a3(UDhmk`?@2DKuQ%$6i5)jtTnJs49xi)ldLo4+VlWbyV(3jCtMTaY;Djft zmx8yxSEF|CD7c>`Y|5Zfes)UsKdq0w4QMB5=;&t2_yoh*S0OGnsZaB3$c>3H=}= z+ooO;I(2|X+NphBpkW#`p0k^rKV`Q#N@y$S6o+#<{pf1UAXSuJ3?`@`8Dki8sO#YPcZlYqn+Y;^Q!M88aC_<#erQY?TOdgN0nD zCVD~DL0rZFCjbUPRbj$hHcH|YxiW(|%pMN06oRQ~C}R-KI=`zXQ_y{_@2Em{p~~DP z03Y4ib%XejQ3bL?S90a)1hYe+`Z7YG4uRUY6AHYd5eg*#)&52It2+YF zpZyR~LpTxt&Zw-z>`3#lPSbW$s zCs3{7(We}g%t#+wmE`FF7nmKbX?5ga%CoTPRQx$x6~hIP9KY6`YSN}pAzN*f?KZ`Q z{{7ea06C+$%mIRo0!bPI8uQy2NWBZD#vmHUDTtQirXqaHvk?BQWtdP_dW`XD#RTJ3 z1_%v)5esHxU(kpfic?4_| ziP`1mX}iXfEz@HV4GWA!OR3Y}d}<02!E96+QC514$ypUXj#~ZsbJi?}P0iOVi)ZJW zmgNhJ?P^JCKQ{R+|Lz1oVf=9cAApR4f$>BR>4-GylwX_r6f9N^b4F(ChB@P-Ovq)3 zk2~^jme}QARB3->`9OaAAGlNM2a+M}FZeIq_y;6*`p-+qm-%(ZMa_V(OF&1?XvV@@ zHl0nQbXjP6@8gi0g_y%?SH69|n>igK5%$N`mrtL4sddr@D`}f1`rr;$787>N!Bs)Y zY1_ikD~pkOzLWWWbxPu(;w-aX9O?Wye>{L^Hip;l(!`iJM>z83+MWkJEj=S1bhltv zYofG+Qtq~=M$C0dK=CELh<^SYFitDLKdit*F=mBil&%OnQMO{6F8uKj2xg)Rv*P4! zX85=?VL6edxWYm;@2ELvFc4o!)Q04+r)Cxjl~G#DqgY_Ph35({B2MD{C90I^2SC(O zzM+!NZj>Hq|74U{tY+~1yrp#q6G7Z}j@DXgRuNTp-io^i#t{<+Oboz=)Qn;t=V!6{ zqU@sNq@C1ezcZt|pmZ6v-4%7-c0u(Yh`n7gf?kX;kRt2tt{U zCzm9TRsCwJ@xsw}2gjWrANOT8PwM>oOMpnaEg$wy81LI`TLBcy>G`M!C)wfYtmW9A z`tFry!ualyRm!;k4tL5*3@m2b>_+**f`Th$1G@5HzcV5{W@Ry!%mv@j3oNg~BVFEb z=C`%&0PJ8F`z1WOmJI={f~%bNeDliV#X_)m7+T2rn^ww(p1K^$aqP-Z34<>bUd4Gz z?W2s|FD}JhaP`1`^G~I96h$O0G!B^$7ZoVn+a%Dl^A_x>!kfZJe(Y8$Q_37`7r}3- zoFpR@?6-VmSY%f9g-{aOIB`d?@RT<_RZ6F;Z3E)P@dXUZj}FN?Vx+rL5)O3cubP$o zMBLwVTi?G6Ll{Un&CQnvi&CD-s;P$6M1Mhn-%}{487Sp(!yUw>hA2xZ&9&O4%j??w z{sct7t|i+|bA8?zFE~%{eRivTQ(}r+<_txzKa~0OjHKM+x}hKa#mn{a%S@>w7U`7L z@vJY$=4scGEr&j1b+@}1BZi{N=2>GThdyR5PsMhmK!W6$LquXT4xKRSMQXuPwbZwU z{6&dM71Zw;aA>*eazi60ZIblIbu;bBRfy|UOx-5Aok_%`F&W-eI=CjKaXi-^TiBe` zOy!Z~pWgKW10lbYta(O@#mGlmeztOcH;gs8*@ro- zIA7W|OCD&vnf=aaZmy|AuP=U#szGha+EYx4E7dnD&(?xtAt^9b*A&m9OOnG%zD&(S zth0Uv!u$hkW83HpYH7aqXCk2y+Cpf5ku~^|Y$CC;5}h|Y0c5W`eR8kcOelV^bp?p-xwJsHiyD#5i^B^L6nip&z4--pI8)Y}C^y0Dn`3WF;v zRzymkS!gxFvtAVvt?2^i7@7Bw^BNoB7P3eTq2hYGwY;x5JU*s8Xvr;=1olsljI=Uf zzOtLGS*UOu*}8%kQTsP;LOek+K1@Fj4=`(?wG$YE4YJPw5`-~n%Hu`!_<8C5;ZbTg3+c8_fp2x#&FlD5pEoy2}zTbyMBI@#z~DB3-%w)4`d$O3+4 zl3>3B3Fz$v+X0(|5M|R(u(OM8S7NG}#=pS0WZdz?T!bf_0G>~a?derr44qX6`e5^zmbZLiG3xd4HheI$MTItEKh=Li-S}~SXrxca zQ_jKOvB9&hjjVu8RVjJYaj~(?&a9tgr?qZ38bmYIj?#J!TOg73A*kaA&`;avJV zv$~N=$tZmuYT~8)^%68_tngMPkQfTURR)SLXD`zS|wwzYRt-4eSae6uT&=&T(tQD4P z$UI90XNg`r2@8~lA7kk*$4wC=xWN&*rf2ftZaIu8AAO&kex01z6QJ+43F}VMn?%T8 z`=OhrEG5w=!mjFKmf^O&;#p_axGnvPJy8z6cE?n#L$bFZVR{_is^5S!`u@Y-#N@E@ z)KvBHO{H_~(Vht_6FYqV$#!#JCP`UyCsL9V&Tcawvv6O=LlD)ZV?t?2yLJ>-nue9J z@x*tf18VtORTOrz&dS1__lS8ec2oy7Z$nGp^5f&Mu){6f(G{bc`k0Bz_^s%3B&E$@ zXAB_?HS5HXq?g-VM6UcC$HoigDZ*cs4U2 z{W?5h(7d4$=ivvBr8Pmi8&9%DOnKVC?(W`>Nv(?X-Btp_F}fBENTZU5HqlAB_Xntc z9&|DgYpoRC+fx??~{@kA&t-?)|G>$3$xUTW#7x6 z|mg z*-oyCVff!(?^1)lOd6?q{$tvTW7O9qo{xO+MMwaEkF?i+Ey4epwj%8MQQu)`WcaW2 zm1X-hI+UPu&RG{-M;qvE5;z0SSyJo4Md?p`h&9t7)I`Hzot`9PJMk>Q4?)uD=P&87 z>RSNeU@xj1EC_m)M=%D0OwJE3}nFk&{26n!| z7TM+AaWWBb%EBTq+HwKH;*iDofjRd_TnuL1^#Tgj6Is%&Su6j8|i%h+mo!U6evxy=TnW?Ym zo=WrXveM&EA(XqJ0#PP3PhyW7wa?oXej0>XDy3i@sU(y6&yDE06QSWmN<2Dv8&R*Kj%Kn|zN<6s(2>KC8?xP1vh%|7dhR zovl$KA4YfcVRZj9;{P-{al?P7OD)^4{9$i#E*A%Q0pnQk(Pl_9+byTS>&X2$5v?*+7y#eRjZJ?_aUY`E;p}K=S3M zV4LKDGaKMH7u1@%zV)fv%LFpJ>jMQNkxrm&`1Glx3YyvqurnA;R3(bwoh+ROU)ky= z_ehxo<%+WnpT{RAW9`0l;M<9S#j|-FiV$HLO)LSMAdrb^d^N++vl+tS=~2Q)fdy!0 z{no=`zJguvqpbwZgbiX}qMr9iH^ZxKP5%w0UVKz9&n&GDOT;_@*#^ByQL(Ate-20O zUgZ;k=+kbV2wYjT(K(gT-BEI!a3H^w2aN1qnK@8a>_z~|UhI^k1ZCKoPaK+^8E?sH z`%P07Ep0uhbZfKI86H9V(%jOBvP-Cz-W%oRFnki1x$Nv*zZ_H?ozfR@p75e5whut(mD z5-@-Ll0mqL=UaM#Kk4)C6u3kCNZr0S=~)5HYtVH!duX0@ehZH>V|pIuz~a6fxP9#+ zOC7CnHe4xe(`wq0eg?2sq4%8J@cJ>A{PzND$(|*g#fRPLeJrqI|D&^WFw?g*`j@+t zw#ogKF8TS?o3EpAU@w$dIG$fm;1Psx&KRs5iHlYW~>V+Cr6B%1AfS8pSw zyYyMWxH|^>bDcVoJ@R*-&jrXicNRBSFKlYxyEsaaQqB4o4>B|yJ0BknoCu)@!jsJ? zn(du6*ec3g$6W!0jCK_!7Ra4wqUal__G4GDDUzp^&(~mwsy0qRb|3Pj{c@uN_O+~; zb{Bf?I*T~u!zpn-KTRVEe!;)%!ZfB}jzp*ShY1?H#MO4@pN2r-(i3VaI@Fy5dM@I~ zUM5EnnkN_^jNx%>>SvRmBUz6ml5Wl0XOnMCm*SOOC*%tguw*lWOD!+9Ts}q4Z~zVG zv?6x8m_Mn``*9lm#V0|5Ok0?AjT4|(I3h=&J;IevmKlUy8VuRGdJb~3KwNs-!jX=$ z;wDbyTrg_#>1c-Bb-_ks=?w0=$7o@Gr2;f-?~KZO>SW84ycUebu&#~()y<+`6^p8X zdGA3BD3YP{-DO04GV;}2fG6GR!w>dUw#6;8U!8{%pKO2l!7v_zmrfJh&;TfFR=kAN z@V(ef#xgJ=Tp#atSOcohV8*A>7dX?k<>nL1sFR3R`r?yvPxQ&qKGK@8aNmWTw5u28 zMHS4`KD;0gRgs&&dlEYP$o(R7k4-MTny>CGU|C5HP1>3^`dC){4>}5x z@T@xtY$)(DX1_a#>>mFaz<0prJdWk`(%)?X1*OjdS%}QwPD)G~&Ia>N*rv8k3-6!i{f}zs-nqpZ_QQHdU;zL= zR{j1pAsc^GS}N-O+cZyySKTPO7=}@utV} ztP_163~{Rfi2IEB*1;IP@(P?+*nk2MA`QQst+8`BoHZMK!dXYK)D)ya$TgTeh=#f1x#)z@CERd%WNhIk(LBhXV z29jsSWR&qy^mfxn?AoM3cPAi%#-GN~BUVYYSOu;P*j~-9ViO_Y(%X*ZqrARF3sxmy zpp>seKvi#mRUdGz+oB2(DB?!e+(Ps@z#2GY_JM$6N2W@AHfJQs#gU+PIq?v>z~$@2 z_E`nM2)HP~4${$AH8;Wq7hbmov%o=_w{<2`07i)UHX}oBonM;22m~pQKf^(IB2v;B zq=8tD2q{X7G{Tl_5SUkDUDSigUe!_hdMwHl!0M)H)6GSt(LE%Bf>3`2GfptMZaRi9 zYL^LE)h1g@d5E=D?G_4wG5|XEP30SaxjF}#6d<}R@IHHkR)7H?g0_;JS5hY*gH-7Q z^OhLnqJnaFQMYB#kfr$QgU9>L+iDU#301ZfpNX^}=U3g;hQiGK%7jW@(_QGqSZ)DU zY{>Yc(EQ}pud7G)R+ChO6HAVsyi?r_JBjQgX-0jegu*ha+c%E#n0Dsu@M4Z|ra-nd z)1J6D`G^*av=tf(5Staya+`))Ax?&I2v0}keV)M$oV`jD7bhJv`IQ#N_g9w`Vw(o4@I5q0sJNSB(=N!XZv~#9_ZPaxtoO9qp-^jPGn|x+45q@u&A87h6=DE(d z>#?9s+SrSU){Q|`?GZ8qnKx_!Qe+nttJpBz^7?RgcfDx;X2a%9<^~&OA^qcVR+kNy zlry6f*MbccW1PL@a6X=h9JidiTvQ7#<1CZslo<~9HuT^w$a`<97^ZHS<6T-I#W+{% zruFbsy(BwA@IMy@-0#J%{&Mi2J{KZ9|&dL=*y(UHaeW!v5C$ghRFDLj}w#4+vBsLs2hm!1%5WKx*ZqXTS9s9 zpBmmqqb6&Y?(4d_`UAJBZYQ%}3)ctX(b?uttBoL-no}L395iL3gVrJ>oOKgoIaBTy{A`^TS1|dWQ z32#Dnc*xTMYHTpTJm>70%p=lE@JyK@4V7-irkQ>T3hoG$J8}3~u~8n1Op&7mRFN1ycppN`m?>%-(2uhF#+%=3@MMyW~w1#g>J@ zX%e;JZoupv=srZBQ#})4z#3NGMOLqwVR3CkUN~IJnkc=qFsQ%`X9?8936S1ymdW)R z{5S{WJ|yg#?$(7FfF67mDcyy^G~G_Buh`;xq#$^MQg5?yznv-ilJ3?Y5|*!!bX-)f zhqPj7S>NBGY*oNT2!o8$+|I-qnaHiF60w6^ZX{CVJomIUMzy(j7(#hnwo$s`9Qm4i za7IQ#B=*%8I#36?bf_WNhCsV4v-x47cUn8qpvg z_4G#JrNPB@n;RtW)QaSDWzEanXuYk{951_Za{avErwvN0D^)80f_-2PV`=CT?lFTb zn2ZNz&;#Hi3)Vq!H~&U|ow_y=#y(;oFh~&5>#8X`A`*ajmAUijmQ2sVbW6v83rZYm zMvmVgQ8NQO%1`D{RDOO%hlQ3Pecfs5usG}BWp*#?9~A}(4K%C6hcS+P7$d^pjZx3m z_LubiKU@5`Z6BX>`9EG_&_;9#FLyR)h6s@pfoLAcb`guUQfp*`1U=YbsZj4d8B|9Q zg3WU}_EnV8qIbQ6aT!i5W!weEsN04WW1bpq10hJsoOj`N<}m; zi*&LzN+TfNh~*L|MSt zP)jY@5Sswll&TGc5Gkg<9@?5fX}V4K8>SRU<%zr&n3|4GBify>#;;sehp5>w7V%m9 zg}}5v(ga*)oEVr;3}`~g0X9)*A|MtNFfzJUi_603kXF05fb?4SC0JaZ~1Hc4=*5*mHbbF|9s`aUr+#m?hlRqw-+G% z4*b0q{4ePCN5RT}Rfhi#|IZ4=zrX+hVKD!O|G(5L{?7D!UD;n;cpnPqj|=%Ta`J2N z{to}W#O5!!{KwP9KNj5lPVswM@?R8s9}&YpDE^+L{5$;j{KdcEF1UZf|0kRAcku7A z;lIGDbpHnb7AgLn;r9UNUkvl~|7Q5}{{21N`8&<;A*;V=B$$4q`F8;8cZT1C4}UTA zu>8jG$A$b)DB^db-=7fwA~I(GH_?AQFa8()H Date: Thu, 14 Mar 2024 14:13:03 +0100 Subject: [PATCH 010/134] Add debug logging to ObjectResolver and simplify ObjectResolverRegistry The debug logging feature has been added to the ObjectResolver class for better tracking of expression resolution. Additionally, the ObjectResolverRegistry has been simplified by directly returning the resolution if the resolver can handle the given object, improving code readability and efficiency. --- .../typeresolver/ObjectResolverRegistry.java | 11 +++----- .../docxstamper/api/ObjectResolver.java | 26 +++++++++++++------ 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/ObjectResolverRegistry.java b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/ObjectResolverRegistry.java index a6c99dca..ecbed2bf 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/ObjectResolverRegistry.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/ObjectResolverRegistry.java @@ -48,13 +48,8 @@ public R resolve( Object object ) { for (ObjectResolver resolver : resolvers) - if (resolver.canResolve(object)) { - R resolution = resolver.resolve(document, expression, object); - var msg = "Expression '{}' replaced by '{}' with resolver {}"; - log.debug(msg, expression, resolution, resolver); - return resolution; - } - String message = "No resolver found for %s".formatted(object); - throw new DocxStamperException(message); + if (resolver.canResolve(object)) + return resolver.resolve(document, expression, object); + throw new DocxStamperException("No resolver for %s".formatted(object)); } } diff --git a/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java b/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java index e3588106..04ce647a 100644 --- a/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java +++ b/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java @@ -2,6 +2,8 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.wickedsource.docxstamper.api.DocxStamperException; import pro.verron.docxstamper.core.Expression; @@ -15,13 +17,6 @@ * @since 1.6.7 */ public interface ObjectResolver { - /** - * Checks if the given object can be resolved. - * - * @param object the object to be resolved - * @return true if the object can be resolved, false otherwise - */ - boolean canResolve(Object object); /** * Resolves the expression in the given document with the provided object. @@ -38,7 +33,22 @@ default R resolve( Expression expression, Object object ) { - return resolve(document, expression.inner(), object); + R resolution = resolve(document, expression.inner(), object); + var msg = "Expression '{}' replaced by '{}' with resolver {}"; + Log.log.debug(msg, expression, resolution, this); + return resolution; + } + + /** + * Checks if the given object can be resolved. + * + * @param object the object to be resolved + * @return true if the object can be resolved, false otherwise + */ + boolean canResolve(Object object); + + class Log { + static Logger log = LoggerFactory.getLogger(ObjectResolver.class); } /** From 52aaa74d77cb8fa29b21f9497dd577f79f01d790 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 14 Mar 2024 14:17:23 +0100 Subject: [PATCH 011/134] Make ObjectResolverRegistry class final The ObjectResolverRegistry class has been made final to prevent further subclasses. This change empowers better design and enforces the existing architecture, which does not require extension of this class. The removal of the logger has also simplified the class, improving readability and promoting efficient code. --- .../replace/typeresolver/ObjectResolverRegistry.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/ObjectResolverRegistry.java b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/ObjectResolverRegistry.java index ecbed2bf..34419280 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/ObjectResolverRegistry.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/ObjectResolverRegistry.java @@ -2,8 +2,6 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.wickedsource.docxstamper.api.DocxStamperException; import pro.verron.docxstamper.api.ObjectResolver; import pro.verron.docxstamper.core.Expression; @@ -18,9 +16,7 @@ * @version ${version} * @since 1.6.7 */ -public class ObjectResolverRegistry { - private static final Logger log = LoggerFactory.getLogger( - ObjectResolverRegistry.class); +public final class ObjectResolverRegistry { private final List resolvers = new ArrayList<>(); /** From d6f591fceb7ec7553724d12083659ffd2a39e1f7 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 14 Mar 2024 14:29:08 +0100 Subject: [PATCH 012/134] This commit includes downgrading the visibility of various resolver classes to package-private, and moving a static method (createRunWithImage) from ImageResolver to RunUtill class. This organize and centralize static methods in a dedicated utility class, effectively improving searchability of methods within the project. Additionally, certain classes and methods have been marked as deprecated to signal upcoming changes in future versions. --- .../wickedsource/docxstamper/DocxStamper.java | 2 +- .../replace/PlaceholderReplacer.java | 2 +- .../docxstamper/util/DocumentUtil.java | 5 +- .../docxstamper/util/RunUtil.java | 309 +++++++++++------- .../resolver => api}/StringResolver.java | 3 +- .../core}/ObjectResolverRegistry.java | 3 +- .../preset/resolver/DateResolver.java | 3 +- .../preset/resolver/ImageResolver.java | 77 +---- .../preset/resolver/LocalDateResolver.java | 4 +- .../resolver/LocalDateTimeResolver.java | 4 +- .../preset/resolver/LocalTimeResolver.java | 4 +- .../preset/resolver/Null2DefaultResolver.java | 4 + .../resolver/Null2PlaceholderResolver.java | 2 +- .../preset/resolver/ToStringResolver.java | 2 +- .../resolver/CustomTypeResolver.java | 2 +- 15 files changed, 219 insertions(+), 207 deletions(-) rename src/main/java/pro/verron/docxstamper/{preset/resolver => api}/StringResolver.java (95%) rename src/main/java/{org/wickedsource/docxstamper/replace/typeresolver => pro/verron/docxstamper/core}/ObjectResolverRegistry.java (94%) diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java index 50843470..fda7f4af 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java @@ -13,11 +13,11 @@ import org.wickedsource.docxstamper.el.StandardMethodResolver; import org.wickedsource.docxstamper.processor.CommentProcessorRegistry; import org.wickedsource.docxstamper.replace.PlaceholderReplacer; -import org.wickedsource.docxstamper.replace.typeresolver.ObjectResolverRegistry; import pro.verron.docxstamper.OpcStamper; import pro.verron.docxstamper.StamperFactory; import pro.verron.docxstamper.api.ObjectResolver; import pro.verron.docxstamper.core.Expressions; +import pro.verron.docxstamper.core.ObjectResolverRegistry; import java.io.InputStream; import java.io.OutputStream; diff --git a/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java b/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java index ffe5cb2e..89c66330 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java @@ -11,12 +11,12 @@ import org.springframework.expression.spel.SpelParseException; import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.el.ExpressionResolver; -import org.wickedsource.docxstamper.replace.typeresolver.ObjectResolverRegistry; import org.wickedsource.docxstamper.util.ParagraphWrapper; import org.wickedsource.docxstamper.util.RunUtil; import org.wickedsource.docxstamper.util.walk.BaseCoordinatesWalker; import pro.verron.docxstamper.core.Expression; import pro.verron.docxstamper.core.Expressions; +import pro.verron.docxstamper.core.ObjectResolverRegistry; /** * Replaces expressions in a document with the values provided by the {@link ExpressionResolver}. diff --git a/src/main/java/org/wickedsource/docxstamper/util/DocumentUtil.java b/src/main/java/org/wickedsource/docxstamper/util/DocumentUtil.java index 398c2b22..1685abbd 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/DocumentUtil.java +++ b/src/main/java/org/wickedsource/docxstamper/util/DocumentUtil.java @@ -9,7 +9,6 @@ import org.docx4j.openpackaging.parts.relationships.RelationshipsPart; import org.docx4j.wml.*; import org.wickedsource.docxstamper.api.DocxStamperException; -import pro.verron.docxstamper.preset.resolver.ImageResolver; import java.util.*; import java.util.stream.Stream; @@ -170,7 +169,9 @@ public static Map walkObjectsAndImportImages( byte[] imageData = docxImageExtractor.getRunDrawingData(currentR); Integer maxWidth = docxImageExtractor.getRunDrawingMaxWidth(currentR); BinaryPartAbstractImage imagePart = tryCreateImagePart(target, imageData); - replacements.put(currentR, ImageResolver.createRunWithImage(maxWidth, imagePart)); + replacements.put(currentR, + RunUtil.createRunWithImage(maxWidth, + imagePart)); } else if (currentObj instanceof ContentAccessor contentAccessor) queue.addAll(contentAccessor.getContent()); } diff --git a/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java b/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java index 8d9a11d3..efc45f07 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java +++ b/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java @@ -1,12 +1,15 @@ package org.wickedsource.docxstamper.util; import jakarta.xml.bind.JAXBElement; +import org.docx4j.dml.wordprocessingDrawing.Inline; import org.docx4j.jaxb.Context; import org.docx4j.model.styles.StyleUtil; +import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage; import org.docx4j.wml.*; import org.wickedsource.docxstamper.api.DocxStamperException; import java.util.Objects; +import java.util.Random; /** * Utility class to handle runs. @@ -17,124 +20,194 @@ * @since 1.0.0 */ public class RunUtil { + private static final Random random = new Random(); private static final String PRESERVE = "preserve"; - private static final ObjectFactory factory = Context.getWmlObjectFactory(); - - private RunUtil() { - throw new DocxStamperException("Utility class shouldn't be instantiated"); - } - - /** - * Returns the text string of a run. - * - * @param run the run whose text to get. - * @return {@link String} representation of the run. - */ - public static String getText(R run) { - StringBuilder result = new StringBuilder(); - for (Object content : run.getContent()) { - if (content instanceof JAXBElement) { - result.append(getText((JAXBElement) content)); - } else if (content instanceof Text text) { - result.append(getText(text)); - } - } - return result.toString(); - } - - private static CharSequence getText(JAXBElement content) { - Object elementValue = content.getValue(); - if (elementValue instanceof Text text) - return getText(text); - if (elementValue instanceof R.Tab) - return "\t"; - return ""; - } - - private static CharSequence getText(Text text) { - String value = text.getValue(); - String space = text.getSpace(); - return Objects.equals(space, PRESERVE) - ? value // keeps spaces if spaces are to be preserved (LibreOffice seems to ignore the "space" property) - : value.trim(); // trimming value if spaces are not to be preserved (simulates behavior of Word;) - } - - /** - * Creates a new run with the given object as content. - * - * @param content the content of the run. - * @return the newly created run. - */ - public static R create(Object content) { - R run = factory.createR(); - run.getContent().add(content); - return run; - } - - /** - * Creates a new run with the specified text and inherits the style of the parent paragraph. - * - * @param text the initial text of the run. - * @param parentParagraph the parent paragraph whose style to inherit. - * @return the newly created run. - */ - public static R create(String text, P parentParagraph) { - R run = create(text); - applyParagraphStyle(parentParagraph, run); - return run; - } - - /** - * Creates a new run with the specified text. - * - * @param text the initial text of the run. - * @return the newly created run. - */ - public static R create(String text) { - R run = factory.createR(); - setText(run, text); - return run; - } - - /** - * Applies the style of the given paragraph to the given content object (if the content object is a Run). - * - * @param p the paragraph whose style to use. - * @param run the Run to which the style should be applied. - */ - public static void applyParagraphStyle(P p, R run) { - if (p.getPPr() != null && p.getPPr().getRPr() != null) { - RPr runProperties = new RPr(); - StyleUtil.apply(p.getPPr().getRPr(), runProperties); - run.setRPr(runProperties); - } - } - - /** - * Sets the text of the given run to the given value. - * - * @param run the run whose text to change. - * @param text the text to set. - */ - public static void setText(R run, String text) { - run.getContent().clear(); - Text textObj = createText(text); - run.getContent().add(textObj); - } - - /** - * Creates a text object with the given text. - * - * @param text the text to set. - * @return the newly created text object. - */ - public static Text createText(String text) { - Text textObj = factory.createText(); - textObj.setValue(text); - textObj.setSpace(PRESERVE); // make the text preserve spaces - return textObj; - } - - + private static final ObjectFactory factory = Context.getWmlObjectFactory(); + + private RunUtil() { + throw new DocxStamperException("Utility class shouldn't be instantiated"); + } + + /** + * Returns the text string of a run. + * + * @param run the run whose text to get. + * @return {@link String} representation of the run. + */ + public static String getText(R run) { + StringBuilder result = new StringBuilder(); + for (Object content : run.getContent()) { + if (content instanceof JAXBElement) { + result.append(getText((JAXBElement) content)); + } else if (content instanceof Text text) { + result.append(getText(text)); + } + } + return result.toString(); + } + + private static CharSequence getText(JAXBElement content) { + Object elementValue = content.getValue(); + if (elementValue instanceof Text text) + return getText(text); + if (elementValue instanceof R.Tab) + return "\t"; + return ""; + } + + private static CharSequence getText(Text text) { + String value = text.getValue(); + String space = text.getSpace(); + return Objects.equals(space, PRESERVE) + ? value // keeps spaces if spaces are to be preserved (LibreOffice seems to ignore the "space" property) + : value.trim(); // trimming value if spaces are not to be preserved (simulates behavior of Word;) + } + + /** + * Creates a new run with the given object as content. + * + * @param content the content of the run. + * @return the newly created run. + */ + public static R create(Object content) { + R run = factory.createR(); + run.getContent() + .add(content); + return run; + } + + /** + * Creates a new run with the specified text and inherits the style of the parent paragraph. + * + * @param text the initial text of the run. + * @param parentParagraph the parent paragraph whose style to inherit. + * @return the newly created run. + */ + public static R create(String text, P parentParagraph) { + R run = create(text); + applyParagraphStyle(parentParagraph, run); + return run; + } + + /** + * Creates a new run with the specified text. + * + * @param text the initial text of the run. + * @return the newly created run. + */ + public static R create(String text) { + R run = factory.createR(); + setText(run, text); + return run; + } + + /** + * Applies the style of the given paragraph to the given content object (if the content object is a Run). + * + * @param p the paragraph whose style to use. + * @param run the Run to which the style should be applied. + */ + public static void applyParagraphStyle(P p, R run) { + if (p.getPPr() != null && p.getPPr() + .getRPr() != null) { + RPr runProperties = new RPr(); + StyleUtil.apply(p.getPPr() + .getRPr(), runProperties); + run.setRPr(runProperties); + } + } + + /** + * Sets the text of the given run to the given value. + * + * @param run the run whose text to change. + * @param text the text to set. + */ + public static void setText(R run, String text) { + run.getContent() + .clear(); + Text textObj = createText(text); + run.getContent() + .add(textObj); + } + + /** + * Creates a text object with the given text. + * + * @param text the text to set. + * @return the newly created text object. + */ + public static Text createText(String text) { + Text textObj = factory.createText(); + textObj.setValue(text); + textObj.setSpace(PRESERVE); // make the text preserve spaces + return textObj; + } + + + /** + * Creates a run containing the given image. + * + * @param maxWidth max width of the image + * @param abstractImage the image + * @return the run containing the image + */ + public static R createRunWithImage( + Integer maxWidth, + BinaryPartAbstractImage abstractImage + ) { + // creating random ids assuming they are unique + // id must not be too large, otherwise Word cannot open the document + int id1 = random.nextInt(100000); + int id2 = random.nextInt(100000); + var filenameHint = "dummyFileName"; + var altText = "dummyAltText"; + + Inline inline = tryCreateImageInline( + filenameHint, + altText, + maxWidth, + abstractImage, + id1, + id2); + + // Now add the inline in w:p/w:r/w:drawing + ObjectFactory factory = new ObjectFactory(); + R run = factory.createR(); + Drawing drawing = factory.createDrawing(); + run.getContent() + .add(drawing); + drawing.getAnchorOrInline() + .add(inline); + + return run; + + } + + private static Inline tryCreateImageInline( + String filenameHint, + String altText, + Integer maxWidth, + BinaryPartAbstractImage abstractImage, + int id1, + int id2 + ) { + try { + return maxWidth == null + ? abstractImage.createImageInline(filenameHint, + altText, + id1, + id2, + false) + : abstractImage.createImageInline(filenameHint, + altText, + id1, + id2, + false, + maxWidth); + } catch (Exception e) { + throw new DocxStamperException(e); + } + } } diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/StringResolver.java b/src/main/java/pro/verron/docxstamper/api/StringResolver.java similarity index 95% rename from src/main/java/pro/verron/docxstamper/preset/resolver/StringResolver.java rename to src/main/java/pro/verron/docxstamper/api/StringResolver.java index 8c284976..40c9ef20 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/StringResolver.java +++ b/src/main/java/pro/verron/docxstamper/api/StringResolver.java @@ -1,9 +1,8 @@ -package pro.verron.docxstamper.preset.resolver; +package pro.verron.docxstamper.api; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; import org.wickedsource.docxstamper.util.RunUtil; -import pro.verron.docxstamper.api.ObjectResolver; /** * This is an abstract class that provides a generic implementation for diff --git a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/ObjectResolverRegistry.java b/src/main/java/pro/verron/docxstamper/core/ObjectResolverRegistry.java similarity index 94% rename from src/main/java/org/wickedsource/docxstamper/replace/typeresolver/ObjectResolverRegistry.java rename to src/main/java/pro/verron/docxstamper/core/ObjectResolverRegistry.java index 34419280..8fa22eaa 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/ObjectResolverRegistry.java +++ b/src/main/java/pro/verron/docxstamper/core/ObjectResolverRegistry.java @@ -1,10 +1,9 @@ -package org.wickedsource.docxstamper.replace.typeresolver; +package pro.verron.docxstamper.core; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; import org.wickedsource.docxstamper.api.DocxStamperException; import pro.verron.docxstamper.api.ObjectResolver; -import pro.verron.docxstamper.core.Expression; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/DateResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/DateResolver.java index 04556a7f..c048aca5 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/DateResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/DateResolver.java @@ -1,6 +1,7 @@ package pro.verron.docxstamper.preset.resolver; import pro.verron.docxstamper.api.ObjectResolver; +import pro.verron.docxstamper.api.StringResolver; import java.text.SimpleDateFormat; import java.time.ZoneId; @@ -15,7 +16,7 @@ * @version ${version} * @since 1.6.7 */ -public final class DateResolver +final class DateResolver extends StringResolver { private final DateTimeFormatter formatter; diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java index 827deccd..9f867d9d 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java @@ -1,17 +1,12 @@ package pro.verron.docxstamper.preset.resolver; -import org.docx4j.dml.wordprocessingDrawing.Inline; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; -import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage; -import org.docx4j.wml.Drawing; -import org.docx4j.wml.ObjectFactory; import org.docx4j.wml.R; import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.replace.typeresolver.image.Image; +import org.wickedsource.docxstamper.util.RunUtil; import pro.verron.docxstamper.api.ObjectResolver; -import java.util.Random; - import static org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage.createImagePart; /** @@ -24,75 +19,9 @@ * @version ${version} * @since 1.6.7 */ -public class ImageResolver +class ImageResolver implements ObjectResolver { - private static final Random random = new Random(); - - /** - * Creates a run containing the given image. - * - * @param maxWidth max width of the image - * @param abstractImage the image - * @return the run containing the image - */ - public static R createRunWithImage( - Integer maxWidth, - BinaryPartAbstractImage abstractImage - ) { - // creating random ids assuming they are unique - // id must not be too large, otherwise Word cannot open the document - int id1 = random.nextInt(100000); - int id2 = random.nextInt(100000); - var filenameHint = "dummyFileName"; - var altText = "dummyAltText"; - - Inline inline = tryCreateImageInline(filenameHint, - altText, - maxWidth, - abstractImage, - id1, - id2); - - // Now add the inline in w:p/w:r/w:drawing - ObjectFactory factory = new ObjectFactory(); - R run = factory.createR(); - Drawing drawing = factory.createDrawing(); - run.getContent() - .add(drawing); - drawing.getAnchorOrInline() - .add(inline); - - return run; - - } - - private static Inline tryCreateImageInline( - String filenameHint, - String altText, - Integer maxWidth, - BinaryPartAbstractImage abstractImage, - int id1, - int id2 - ) { - try { - return maxWidth == null - ? abstractImage.createImageInline(filenameHint, - altText, - id1, - id2, - false) - : abstractImage.createImageInline(filenameHint, - altText, - id1, - id2, - false, - maxWidth); - } catch (Exception e) { - throw new DocxStamperException(e); - } - } - /** * Resolves an image and adds it to a {@link WordprocessingMLPackage} * document. @@ -106,7 +35,7 @@ public R resolve(WordprocessingMLPackage document, Image image) { try { // TODO: adding the same image twice will put the image twice into the docx-zip file. make the second // addition of the same image a reference instead. - return createRunWithImage( + return RunUtil.createRunWithImage( image.getMaxWidth(), createImagePart(document, image.getImageBytes()) ); diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateResolver.java index d622a572..1387929f 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateResolver.java @@ -1,5 +1,7 @@ package pro.verron.docxstamper.preset.resolver; +import pro.verron.docxstamper.api.StringResolver; + import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -10,7 +12,7 @@ * @version ${version} * @since 1.6.4 */ -public final class LocalDateResolver +final class LocalDateResolver extends StringResolver { private final DateTimeFormatter formatter; diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateTimeResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateTimeResolver.java index d01a642c..1fb265f3 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateTimeResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateTimeResolver.java @@ -1,5 +1,7 @@ package pro.verron.docxstamper.preset.resolver; +import pro.verron.docxstamper.api.StringResolver; + import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -10,7 +12,7 @@ * @version ${version} * @since 1.6.4 */ -public final class LocalDateTimeResolver +final class LocalDateTimeResolver extends StringResolver { private final DateTimeFormatter formatter; diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/LocalTimeResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/LocalTimeResolver.java index dd056bfc..f55d1960 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/LocalTimeResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/LocalTimeResolver.java @@ -1,5 +1,7 @@ package pro.verron.docxstamper.preset.resolver; +import pro.verron.docxstamper.api.StringResolver; + import java.time.LocalTime; import java.time.format.DateTimeFormatter; @@ -10,7 +12,7 @@ * @version ${version} * @since 1.6.4 */ -public final class LocalTimeResolver +final class LocalTimeResolver extends StringResolver { private final DateTimeFormatter formatter; diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/Null2DefaultResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/Null2DefaultResolver.java index bb2ca14c..9f16aebd 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/Null2DefaultResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/Null2DefaultResolver.java @@ -10,10 +10,14 @@ * {@link ObjectResolver} interface * that resolves null objects by creating a run with a default text value. * + * @deprecated will not be removed, but will be made package-private + * * @author Joseph Verron * @version ${version} * @since 1.6.7 */ + +@Deprecated(since = "1.6.7") public class Null2DefaultResolver implements ObjectResolver { diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/Null2PlaceholderResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/Null2PlaceholderResolver.java index 3f0873c2..68036249 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/Null2PlaceholderResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/Null2PlaceholderResolver.java @@ -15,7 +15,7 @@ * @version ${version} * @since 1.6.7 */ -public class Null2PlaceholderResolver +class Null2PlaceholderResolver implements ObjectResolver { /* package */ Null2PlaceholderResolver() { diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/ToStringResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/ToStringResolver.java index c3050867..ea1b1d86 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/ToStringResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/ToStringResolver.java @@ -14,7 +14,7 @@ * * @version ${version} * * @since 1.6.7 */ -public class ToStringResolver +class ToStringResolver implements ObjectResolver { @Override public boolean canResolve(Object object) { diff --git a/src/test/java/pro/verron/docxstamper/resolver/CustomTypeResolver.java b/src/test/java/pro/verron/docxstamper/resolver/CustomTypeResolver.java index 8e51ef9f..80a6bdad 100644 --- a/src/test/java/pro/verron/docxstamper/resolver/CustomTypeResolver.java +++ b/src/test/java/pro/verron/docxstamper/resolver/CustomTypeResolver.java @@ -1,6 +1,6 @@ package pro.verron.docxstamper.resolver; -import pro.verron.docxstamper.preset.resolver.StringResolver; +import pro.verron.docxstamper.api.StringResolver; import pro.verron.docxstamper.utils.context.Contexts; /** From a3830ec0005ccac564f3dd8df94808fb84bec1ab Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 14 Mar 2024 15:41:47 +0100 Subject: [PATCH 013/134] Refactor into Java 9 modules, reorganize codebase This commit introduces Java 9 modularization, leading to a major restructuring of the codebase. Two files, ProcessorExceptionHandler.java and ThrowingRunnable.java, have been deleted and their logic has been moved into RepeatDocPartProcessor.java. Additionally, multiple files have been renamed in the test directory. --- src/main/java/module-info.java | 18 +++ .../repeat/RepeatDocPartProcessor.java | 108 ++++++++++++++++- .../replace/typeresolver/image/Image.java | 40 +------ .../pro/verron/docxstamper/api/Image.java | 77 ++++++++++++ .../preset/resolver/ImageResolver.java | 2 +- .../utils/ProcessorExceptionHandler.java | 78 ------------ .../docxstamper/utils/ThrowingRunnable.java | 37 ------ src/test/java/module-info.java | 22 ++++ .../docxstamper/{ => test}/DefaultTests.java | 21 ++-- ...ssionReplacementInHeaderAndFooterTest.java | 7 +- .../ExpressionReplacementInTextBoxesTest.java | 7 +- .../FailOnUnresolvedExpressionTest.java | 6 +- .../{replace => test}/IndexedRunTest.java | 2 +- .../{ => test}/MultiSectionTest.java | 7 +- .../{ => test}/MultiStampTest.java | 9 +- .../{ => test}/NullPointerResolutionTest.java | 11 +- .../{util => test}/ObjectDeleterTest.java | 9 +- .../ParagraphWrapperTest.java | 2 +- .../RepeatDocPartBadExpressionTest.java | 113 +++++++++--------- .../{util => test}/RunUtilTest.java | 7 +- .../docxstamper/{ => test}/Functions.java | 2 +- .../{ => test}/SpelInjectionTest.java | 8 +- .../{ => test}/StampTableTest.java | 8 +- .../{ => test}/accessors/SimpleGetter.java | 2 +- .../CustomCommentProcessor.java | 2 +- .../ICustomCommentProcessor.java | 2 +- .../resolver/CustomTypeResolver.java | 4 +- .../{ => test}/utils/DocxCollector.java | 2 +- .../{ => test}/utils/IOStreams.java | 2 +- .../{ => test}/utils/RunCollector.java | 2 +- .../{ => test}/utils/Stringifier.java | 2 +- .../{ => test}/utils/TestDocxStamper.java | 2 +- .../{ => test}/utils/ThrowingSupplier.java | 2 +- .../{ => test}/utils/context/Contexts.java | 16 +-- .../utils/context/NullishContext.java | 14 +-- .../{ => test}/utils/context/SubContext.java | 2 +- 36 files changed, 374 insertions(+), 281 deletions(-) create mode 100644 src/main/java/module-info.java create mode 100644 src/main/java/pro/verron/docxstamper/api/Image.java delete mode 100644 src/main/java/pro/verron/docxstamper/utils/ProcessorExceptionHandler.java delete mode 100644 src/main/java/pro/verron/docxstamper/utils/ThrowingRunnable.java create mode 100644 src/test/java/module-info.java rename src/test/java/org/wickedsource/docxstamper/{ => test}/DefaultTests.java (99%) rename src/test/java/org/wickedsource/docxstamper/{ => test}/ExpressionReplacementInHeaderAndFooterTest.java (93%) rename src/test/java/org/wickedsource/docxstamper/{ => test}/ExpressionReplacementInTextBoxesTest.java (79%) rename src/test/java/org/wickedsource/docxstamper/{ => test}/FailOnUnresolvedExpressionTest.java (87%) rename src/test/java/org/wickedsource/docxstamper/{replace => test}/IndexedRunTest.java (97%) rename src/test/java/org/wickedsource/docxstamper/{ => test}/MultiSectionTest.java (84%) rename src/test/java/org/wickedsource/docxstamper/{ => test}/MultiStampTest.java (90%) rename src/test/java/org/wickedsource/docxstamper/{ => test}/NullPointerResolutionTest.java (74%) rename src/test/java/org/wickedsource/docxstamper/{util => test}/ObjectDeleterTest.java (96%) rename src/test/java/org/wickedsource/docxstamper/{replace => test}/ParagraphWrapperTest.java (95%) rename src/test/java/org/wickedsource/docxstamper/{ => test}/RepeatDocPartBadExpressionTest.java (78%) rename src/test/java/org/wickedsource/docxstamper/{util => test}/RunUtilTest.java (89%) rename src/test/java/pro/verron/docxstamper/{ => test}/Functions.java (95%) rename src/test/java/pro/verron/docxstamper/{ => test}/SpelInjectionTest.java (79%) rename src/test/java/pro/verron/docxstamper/{ => test}/StampTableTest.java (90%) rename src/test/java/pro/verron/docxstamper/{ => test}/accessors/SimpleGetter.java (97%) rename src/test/java/pro/verron/docxstamper/{ => test}/commentProcessors/CustomCommentProcessor.java (97%) rename src/test/java/pro/verron/docxstamper/{ => test}/commentProcessors/ICustomCommentProcessor.java (86%) rename src/test/java/pro/verron/docxstamper/{ => test}/resolver/CustomTypeResolver.java (86%) rename src/test/java/pro/verron/docxstamper/{ => test}/utils/DocxCollector.java (96%) rename src/test/java/pro/verron/docxstamper/{ => test}/utils/IOStreams.java (97%) rename src/test/java/pro/verron/docxstamper/{ => test}/utils/RunCollector.java (93%) rename src/test/java/pro/verron/docxstamper/{ => test}/utils/Stringifier.java (99%) rename src/test/java/pro/verron/docxstamper/{ => test}/utils/TestDocxStamper.java (98%) rename src/test/java/pro/verron/docxstamper/{ => test}/utils/ThrowingSupplier.java (92%) rename src/test/java/pro/verron/docxstamper/{ => test}/utils/context/Contexts.java (96%) rename src/test/java/pro/verron/docxstamper/{ => test}/utils/context/NullishContext.java (85%) rename src/test/java/pro/verron/docxstamper/{ => test}/utils/context/SubContext.java (97%) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java new file mode 100644 index 00000000..f2a7a96e --- /dev/null +++ b/src/main/java/module-info.java @@ -0,0 +1,18 @@ +module pro.verron.opcstamper { + exports org.wickedsource.docxstamper.el; + exports pro.verron.docxstamper.api; + exports pro.verron.docxstamper.preset.resolver; + exports org.wickedsource.docxstamper.processor.table; + exports org.wickedsource.docxstamper; + exports org.wickedsource.docxstamper.util; + exports org.wickedsource.docxstamper.api.commentprocessor; + exports org.wickedsource.docxstamper.processor; + exports org.wickedsource.docxstamper.replace; + exports org.wickedsource.docxstamper.api; + requires org.apache.commons.io; + requires org.docx4j.core; + requires spring.expression; + requires org.slf4j; + requires spring.core; + requires jakarta.xml.bind; +} diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java index 210117d9..8a0d3136 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java @@ -14,7 +14,6 @@ import org.wickedsource.docxstamper.util.ParagraphUtil; import org.wickedsource.docxstamper.util.SectionUtil; import pro.verron.docxstamper.OpcStamper; -import pro.verron.docxstamper.utils.ProcessorExceptionHandler; import java.io.IOException; import java.io.OutputStream; @@ -22,8 +21,10 @@ import java.io.PipedOutputStream; import java.util.*; import java.util.Map.Entry; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Supplier; @@ -304,4 +305,109 @@ private void stamp( public void reset() { contexts.clear(); } + + /** + * A functional interface representing runnable task able to throw an exception. + * It extends the {@link Runnable} interface and provides default implementation + * of the {@link Runnable#run()} method handling the exception by rethrowing it + * wrapped inside a {@link DocxStamperException}. + * + * @author Joseph Verron + * @version ${version} + * @since 1.6.6 + */ + interface ThrowingRunnable + extends Runnable { + + /** + * Executes the runnable task, handling any exception by throwing it wrapped + * inside a {@link DocxStamperException}. + */ + default void run() { + try { + throwingRun(); + } catch (Exception e) { + throw new DocxStamperException(e); + } + } + + /** + * Executes the runnable task + * + * @throws Exception if an exception occurs executing the task + */ + void throwingRun() throws Exception; + } + + /** + * This class is responsible for capturing and handling uncaught exceptions + * that occur in a thread. + * It implements the {@link Thread.UncaughtExceptionHandler} interface and can + * be assigned to a thread using the + * {@link Thread#setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler)} method. + * When an exception occurs in the thread, + * the {@link ProcessorExceptionHandler#uncaughtException(Thread, Throwable)} + * method will be called. + * This class provides the following features: + * 1. Capturing and storing the uncaught exception. + * 2. Executing a list of routines when an exception occurs. + * 3. Providing access to the captured exception, if any. + * Example usage: + * + * ProcessorExceptionHandler exceptionHandler = new + * ProcessorExceptionHandler(){}; + * thread.setUncaughtExceptionHandler(exceptionHandler); + * + * + * @author Joseph Verron + * @version ${version} + * @see Thread.UncaughtExceptionHandler + * @since 1.6.6 + */ + static class ProcessorExceptionHandler + implements Thread.UncaughtExceptionHandler { + private final AtomicReference exception; + private final List onException; + + /** + * Constructs a new instance for managing thread's uncaught exceptions. + * Once set to a thread, it retains the exception information and performs specified routines. + */ + public ProcessorExceptionHandler() { + this.exception = new AtomicReference<>(); + this.onException = new CopyOnWriteArrayList<>(); + } + + /** + * {@inheritDoc} + *

+ * Captures and stores an uncaught exception from a thread run + * and executes all defined routines on occurrence of the exception. + */ + @Override + public void uncaughtException(Thread t, Throwable e) { + exception.set(e); + onException.forEach(Runnable::run); + } + + /** + * Adds a routine to the list of routines that should be run + * when an exception occurs. + * + * @param runnable The runnable routine to be added + */ + public void onException(ThrowingRunnable runnable) { + onException.add(runnable); + } + + /** + * Returns the captured exception if present. + * + * @return an {@link Optional} containing the captured exception, + * or an {@link Optional#empty()} if no exception was captured + */ + public Optional exception() { + return Optional.ofNullable(exception.get()); + } + } } diff --git a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java index 016e5d86..a8071f06 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java @@ -1,8 +1,5 @@ package org.wickedsource.docxstamper.replace.typeresolver.image; -import org.apache.commons.io.IOUtils; - -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -14,10 +11,8 @@ * @version ${version} * @since 1.0.0 */ -public class Image { - - private final byte[] imageBytes; - private Integer maxWidth; +public final class Image + extends pro.verron.docxstamper.api.Image { /** *

Constructor for Image.

@@ -26,9 +21,7 @@ public class Image { * @throws IOException if any. */ public Image(InputStream in) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - IOUtils.copy(in, out); - this.imageBytes = out.toByteArray(); + super(in); } /** @@ -39,10 +32,7 @@ public Image(InputStream in) throws IOException { * @throws IOException if any. */ public Image(InputStream in, Integer maxWidth) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - IOUtils.copy(in, out); - this.imageBytes = out.toByteArray(); - this.maxWidth = maxWidth; + super(in, maxWidth); } /** @@ -51,7 +41,7 @@ public Image(InputStream in, Integer maxWidth) throws IOException { * @param imageBytes - content of the image as array of the bytes */ public Image(byte[] imageBytes) { - this.imageBytes = imageBytes; + super(imageBytes); } /** @@ -61,25 +51,7 @@ public Image(byte[] imageBytes) { * @param maxWidth - max width of the image in twip */ public Image(byte[] imageBytes, Integer maxWidth) { - this.imageBytes = imageBytes; - this.maxWidth = maxWidth; + super(imageBytes, maxWidth); } - /** - *

Getter for the field maxWidth.

- * - * @return a {@link java.lang.Integer} object - */ - public Integer getMaxWidth() { - return maxWidth; - } - - /** - *

Getter for the field imageBytes.

- * - * @return an array of {@link byte} objects - */ - public byte[] getImageBytes() { - return imageBytes; - } } diff --git a/src/main/java/pro/verron/docxstamper/api/Image.java b/src/main/java/pro/verron/docxstamper/api/Image.java new file mode 100644 index 00000000..ae1243e5 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/api/Image.java @@ -0,0 +1,77 @@ +package pro.verron.docxstamper.api; + +import org.apache.commons.io.IOUtils; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +public sealed class Image + permits org.wickedsource.docxstamper.replace.typeresolver.image.Image { + + protected final byte[] imageBytes; + protected Integer maxWidth; + + /** + *

Constructor for Image.

+ * + * @param in - content of the image as InputStream + * @throws IOException if any. + */ + public Image(InputStream in) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + IOUtils.copy(in, out); + this.imageBytes = out.toByteArray(); + } + + /** + *

Constructor for Image.

+ * + * @param in - content of the image as InputStream + * @param maxWidth - max width of the image in twip + * @throws IOException if any. + */ + public Image(InputStream in, Integer maxWidth) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + IOUtils.copy(in, out); + this.imageBytes = out.toByteArray(); + this.maxWidth = maxWidth; + } + + /** + *

Constructor for Image.

+ * + * @param imageBytes - content of the image as array of the bytes + */ + public Image(byte[] imageBytes) { + this.imageBytes = imageBytes; + } + /** + *

Constructor for Image.

+ * + * @param imageBytes - content of the image as array of the bytes + * @param maxWidth - max width of the image in twip + */ + public Image(byte[] imageBytes, Integer maxWidth) { + this.imageBytes = imageBytes; + this.maxWidth = maxWidth; + } + + /** + *

Getter for the field maxWidth.

+ * + * @return a {@link Integer} object + */ + public Integer getMaxWidth() { + return maxWidth; + } + + /** + *

Getter for the field imageBytes.

+ * + * @return an array of {@link byte} objects + */ + public byte[] getImageBytes() { + return imageBytes; + } +} diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java index 9f867d9d..7e571580 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java @@ -3,8 +3,8 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; import org.wickedsource.docxstamper.api.DocxStamperException; -import org.wickedsource.docxstamper.replace.typeresolver.image.Image; import org.wickedsource.docxstamper.util.RunUtil; +import pro.verron.docxstamper.api.Image; import pro.verron.docxstamper.api.ObjectResolver; import static org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage.createImagePart; diff --git a/src/main/java/pro/verron/docxstamper/utils/ProcessorExceptionHandler.java b/src/main/java/pro/verron/docxstamper/utils/ProcessorExceptionHandler.java deleted file mode 100644 index d839fca5..00000000 --- a/src/main/java/pro/verron/docxstamper/utils/ProcessorExceptionHandler.java +++ /dev/null @@ -1,78 +0,0 @@ -package pro.verron.docxstamper.utils; - -import java.util.List; -import java.util.Optional; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicReference; - -/** - * This class is responsible for capturing and handling uncaught exceptions - * that occur in a thread. - * It implements the {@link Thread.UncaughtExceptionHandler} interface and can - * be assigned to a thread using the - * {@link Thread#setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler)} method. - * When an exception occurs in the thread, - * the {@link ProcessorExceptionHandler#uncaughtException(Thread, Throwable)} - * method will be called. - * This class provides the following features: - * 1. Capturing and storing the uncaught exception. - * 2. Executing a list of routines when an exception occurs. - * 3. Providing access to the captured exception, if any. - * Example usage: - * - * ProcessorExceptionHandler exceptionHandler = new - * ProcessorExceptionHandler(){}; - * thread.setUncaughtExceptionHandler(exceptionHandler); - * - * - * @see Thread.UncaughtExceptionHandler - * @author Joseph Verron - * @version ${version} - * @since 1.6.6 - */ -public class ProcessorExceptionHandler - implements Thread.UncaughtExceptionHandler { - private final AtomicReference exception; - private final List onException; - - /** - * Constructs a new instance for managing thread's uncaught exceptions. - * Once set to a thread, it retains the exception information and performs specified routines. - */ - public ProcessorExceptionHandler() { - this.exception = new AtomicReference<>(); - this.onException = new CopyOnWriteArrayList<>(); - } - - /** - * {@inheritDoc} - * - * Captures and stores an uncaught exception from a thread run - * and executes all defined routines on occurrence of the exception. - */ - @Override - public void uncaughtException(Thread t, Throwable e) { - exception.set(e); - onException.forEach(Runnable::run); - } - - /** - * Adds a routine to the list of routines that should be run - * when an exception occurs. - * - * @param runnable The runnable routine to be added - */ - public void onException(ThrowingRunnable runnable) { - onException.add(runnable); - } - - /** - * Returns the captured exception if present. - * - * @return an {@link Optional} containing the captured exception, - * or an {@link Optional#empty()} if no exception was captured - */ - public Optional exception() { - return Optional.ofNullable(exception.get()); - } -} diff --git a/src/main/java/pro/verron/docxstamper/utils/ThrowingRunnable.java b/src/main/java/pro/verron/docxstamper/utils/ThrowingRunnable.java deleted file mode 100644 index 75648953..00000000 --- a/src/main/java/pro/verron/docxstamper/utils/ThrowingRunnable.java +++ /dev/null @@ -1,37 +0,0 @@ -package pro.verron.docxstamper.utils; - - -import org.wickedsource.docxstamper.api.DocxStamperException; - -/** - * A functional interface representing runnable task able to throw an exception. - * It extends the {@link Runnable} interface and provides default implementation - * of the {@link Runnable#run()} method handling the exception by rethrowing it - * wrapped inside a {@link DocxStamperException}. - * - * @author Joseph Verron - * @version ${version} - * @since 1.6.6 - */ -public interface ThrowingRunnable - extends Runnable { - - /** - * Executes the runnable task, handling any exception by throwing it wrapped - * inside a {@link DocxStamperException}. - */ - default void run() { - try { - throwingRun(); - } catch (Exception e) { - throw new DocxStamperException(e); - } - } - - /** - * Executes the runnable task - * - * @throws Exception if an exception occurs executing the task - */ - void throwingRun() throws Exception; -} diff --git a/src/test/java/module-info.java b/src/test/java/module-info.java new file mode 100644 index 00000000..e5159812 --- /dev/null +++ b/src/test/java/module-info.java @@ -0,0 +1,22 @@ +module pro.verron.opcstamper.test { + requires pro.verron.opcstamper; + requires org.junit.jupiter.api; + requires org.junit.jupiter.params; + requires spring.context; + requires spring.expression; + requires org.docx4j.openxml_objects; + requires org.docx4j.core; + requires org.slf4j; + requires jakarta.xml.bind; + + opens pro.verron.docxstamper.test to + spring.expression, org.junit.platform.commons; + + opens org.wickedsource.docxstamper.test to org.junit.platform.commons; + opens pro.verron.docxstamper.test.utils.context to spring.core; + + exports pro.verron.docxstamper.test.utils.context to spring.expression; + exports pro.verron.docxstamper.test.commentProcessors to pro.verron.opcstamper; + exports pro.verron.docxstamper.test to pro.verron.opcstamper; + exports org.wickedsource.docxstamper.test to spring.expression; +} diff --git a/src/test/java/org/wickedsource/docxstamper/DefaultTests.java b/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java similarity index 99% rename from src/test/java/org/wickedsource/docxstamper/DefaultTests.java rename to src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java index 25aae788..e82d4468 100644 --- a/src/test/java/org/wickedsource/docxstamper/DefaultTests.java +++ b/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java @@ -1,4 +1,4 @@ -package org.wickedsource.docxstamper; +package org.wickedsource.docxstamper.test; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; @@ -7,16 +7,17 @@ import org.springframework.context.expression.MapAccessor; import org.springframework.expression.spel.SpelParserConfiguration; import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.wickedsource.docxstamper.DocxStamperConfiguration; import org.wickedsource.docxstamper.el.NoOpEvaluationContextConfigurer; -import org.wickedsource.docxstamper.replace.typeresolver.image.Image; -import pro.verron.docxstamper.Functions; -import pro.verron.docxstamper.accessors.SimpleGetter; -import pro.verron.docxstamper.commentProcessors.CustomCommentProcessor; -import pro.verron.docxstamper.commentProcessors.ICustomCommentProcessor; +import pro.verron.docxstamper.api.Image; import pro.verron.docxstamper.preset.resolver.Resolvers; -import pro.verron.docxstamper.resolver.CustomTypeResolver; -import pro.verron.docxstamper.utils.TestDocxStamper; -import pro.verron.docxstamper.utils.context.Contexts; +import pro.verron.docxstamper.test.Functions; +import pro.verron.docxstamper.test.accessors.SimpleGetter; +import pro.verron.docxstamper.test.commentProcessors.CustomCommentProcessor; +import pro.verron.docxstamper.test.commentProcessors.ICustomCommentProcessor; +import pro.verron.docxstamper.test.resolver.CustomTypeResolver; +import pro.verron.docxstamper.test.utils.TestDocxStamper; +import pro.verron.docxstamper.test.utils.context.Contexts; import java.io.IOException; import java.io.InputStream; @@ -30,7 +31,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.junit.jupiter.params.provider.Arguments.of; -import static pro.verron.docxstamper.utils.context.Contexts.*; +import static pro.verron.docxstamper.test.utils.context.Contexts.*; /** *

DefaultTests class.

diff --git a/src/test/java/org/wickedsource/docxstamper/ExpressionReplacementInHeaderAndFooterTest.java b/src/test/java/org/wickedsource/docxstamper/test/ExpressionReplacementInHeaderAndFooterTest.java similarity index 93% rename from src/test/java/org/wickedsource/docxstamper/ExpressionReplacementInHeaderAndFooterTest.java rename to src/test/java/org/wickedsource/docxstamper/test/ExpressionReplacementInHeaderAndFooterTest.java index ba6a64f5..36f99a70 100644 --- a/src/test/java/org/wickedsource/docxstamper/ExpressionReplacementInHeaderAndFooterTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/ExpressionReplacementInHeaderAndFooterTest.java @@ -1,4 +1,4 @@ -package org.wickedsource.docxstamper; +package org.wickedsource.docxstamper.test; import org.docx4j.openpackaging.exceptions.Docx4JException; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; @@ -9,14 +9,15 @@ import org.docx4j.relationships.Relationship; import org.docx4j.wml.P; import org.junit.jupiter.api.Test; +import org.wickedsource.docxstamper.DocxStamperConfiguration; import org.wickedsource.docxstamper.util.ParagraphWrapper; -import pro.verron.docxstamper.utils.TestDocxStamper; +import pro.verron.docxstamper.test.utils.TestDocxStamper; import java.io.IOException; import java.nio.file.Path; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.wickedsource.docxstamper.DefaultTests.getResource; +import static org.wickedsource.docxstamper.test.DefaultTests.getResource; /** * @author Joseph Verron diff --git a/src/test/java/org/wickedsource/docxstamper/ExpressionReplacementInTextBoxesTest.java b/src/test/java/org/wickedsource/docxstamper/test/ExpressionReplacementInTextBoxesTest.java similarity index 79% rename from src/test/java/org/wickedsource/docxstamper/ExpressionReplacementInTextBoxesTest.java rename to src/test/java/org/wickedsource/docxstamper/test/ExpressionReplacementInTextBoxesTest.java index bd4de44e..83cfaab0 100644 --- a/src/test/java/org/wickedsource/docxstamper/ExpressionReplacementInTextBoxesTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/ExpressionReplacementInTextBoxesTest.java @@ -1,14 +1,15 @@ -package org.wickedsource.docxstamper; +package org.wickedsource.docxstamper.test; import org.docx4j.dml.wordprocessingDrawing.Anchor; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.utils.TestDocxStamper; +import org.wickedsource.docxstamper.DocxStamperConfiguration; +import pro.verron.docxstamper.test.utils.TestDocxStamper; import java.nio.file.Path; import java.util.List; import static org.junit.jupiter.api.Assertions.assertIterableEquals; -import static org.wickedsource.docxstamper.DefaultTests.getResource; +import static org.wickedsource.docxstamper.test.DefaultTests.getResource; /** * @author Joseph Verron diff --git a/src/test/java/org/wickedsource/docxstamper/FailOnUnresolvedExpressionTest.java b/src/test/java/org/wickedsource/docxstamper/test/FailOnUnresolvedExpressionTest.java similarity index 87% rename from src/test/java/org/wickedsource/docxstamper/FailOnUnresolvedExpressionTest.java rename to src/test/java/org/wickedsource/docxstamper/test/FailOnUnresolvedExpressionTest.java index c5faae78..5629efe2 100644 --- a/src/test/java/org/wickedsource/docxstamper/FailOnUnresolvedExpressionTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/FailOnUnresolvedExpressionTest.java @@ -1,7 +1,9 @@ -package org.wickedsource.docxstamper; +package org.wickedsource.docxstamper.test; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.wickedsource.docxstamper.DocxStamper; +import org.wickedsource.docxstamper.DocxStamperConfiguration; import org.wickedsource.docxstamper.api.UnresolvedExpressionException; import java.io.ByteArrayOutputStream; @@ -10,7 +12,7 @@ import java.nio.file.Path; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.wickedsource.docxstamper.DefaultTests.getResource; +import static org.wickedsource.docxstamper.test.DefaultTests.getResource; /** * @author Joseph Verron diff --git a/src/test/java/org/wickedsource/docxstamper/replace/IndexedRunTest.java b/src/test/java/org/wickedsource/docxstamper/test/IndexedRunTest.java similarity index 97% rename from src/test/java/org/wickedsource/docxstamper/replace/IndexedRunTest.java rename to src/test/java/org/wickedsource/docxstamper/test/IndexedRunTest.java index 97b12149..73455392 100644 --- a/src/test/java/org/wickedsource/docxstamper/replace/IndexedRunTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/IndexedRunTest.java @@ -1,4 +1,4 @@ -package org.wickedsource.docxstamper.replace; +package org.wickedsource.docxstamper.test; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/wickedsource/docxstamper/MultiSectionTest.java b/src/test/java/org/wickedsource/docxstamper/test/MultiSectionTest.java similarity index 84% rename from src/test/java/org/wickedsource/docxstamper/MultiSectionTest.java rename to src/test/java/org/wickedsource/docxstamper/test/MultiSectionTest.java index 74df3b4e..8748c573 100644 --- a/src/test/java/org/wickedsource/docxstamper/MultiSectionTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/MultiSectionTest.java @@ -1,19 +1,20 @@ -package org.wickedsource.docxstamper; +package org.wickedsource.docxstamper.test; import org.docx4j.TextUtils; import org.docx4j.openpackaging.exceptions.Docx4JException; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; import org.junit.jupiter.api.Test; +import org.wickedsource.docxstamper.DocxStamperConfiguration; import org.wickedsource.docxstamper.util.DocumentUtil; -import pro.verron.docxstamper.utils.TestDocxStamper; +import pro.verron.docxstamper.test.utils.TestDocxStamper; import java.io.IOException; import java.nio.file.Path; import java.util.List; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.wickedsource.docxstamper.DefaultTests.getResource; +import static org.wickedsource.docxstamper.test.DefaultTests.getResource; /** * @author Joseph Verron diff --git a/src/test/java/org/wickedsource/docxstamper/MultiStampTest.java b/src/test/java/org/wickedsource/docxstamper/test/MultiStampTest.java similarity index 90% rename from src/test/java/org/wickedsource/docxstamper/MultiStampTest.java rename to src/test/java/org/wickedsource/docxstamper/test/MultiStampTest.java index 97a5556c..196d39ff 100644 --- a/src/test/java/org/wickedsource/docxstamper/MultiStampTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/MultiStampTest.java @@ -1,13 +1,14 @@ -package org.wickedsource.docxstamper; +package org.wickedsource.docxstamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.utils.TestDocxStamper; +import org.wickedsource.docxstamper.DocxStamperConfiguration; +import pro.verron.docxstamper.test.utils.TestDocxStamper; import java.nio.file.Path; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.wickedsource.docxstamper.DefaultTests.getResource; -import static pro.verron.docxstamper.utils.context.Contexts.names; +import static org.wickedsource.docxstamper.test.DefaultTests.getResource; +import static pro.verron.docxstamper.test.utils.context.Contexts.names; /** * @author Joseph Verron diff --git a/src/test/java/org/wickedsource/docxstamper/NullPointerResolutionTest.java b/src/test/java/org/wickedsource/docxstamper/test/NullPointerResolutionTest.java similarity index 74% rename from src/test/java/org/wickedsource/docxstamper/NullPointerResolutionTest.java rename to src/test/java/org/wickedsource/docxstamper/test/NullPointerResolutionTest.java index 2b1b3c2a..25a7364e 100644 --- a/src/test/java/org/wickedsource/docxstamper/NullPointerResolutionTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/NullPointerResolutionTest.java @@ -1,17 +1,18 @@ -package org.wickedsource.docxstamper; +package org.wickedsource.docxstamper.test; import org.junit.jupiter.api.Test; +import org.wickedsource.docxstamper.DocxStamperConfiguration; import org.wickedsource.docxstamper.api.DocxStamperException; -import pro.verron.docxstamper.utils.TestDocxStamper; -import pro.verron.docxstamper.utils.context.NullishContext; -import pro.verron.docxstamper.utils.context.SubContext; +import pro.verron.docxstamper.test.utils.TestDocxStamper; +import pro.verron.docxstamper.test.utils.context.NullishContext; +import pro.verron.docxstamper.test.utils.context.SubContext; import java.io.IOException; import java.nio.file.Path; import java.util.List; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.wickedsource.docxstamper.DefaultTests.getResource; +import static org.wickedsource.docxstamper.test.DefaultTests.getResource; /** * @author Joseph Verron diff --git a/src/test/java/org/wickedsource/docxstamper/util/ObjectDeleterTest.java b/src/test/java/org/wickedsource/docxstamper/test/ObjectDeleterTest.java similarity index 96% rename from src/test/java/org/wickedsource/docxstamper/util/ObjectDeleterTest.java rename to src/test/java/org/wickedsource/docxstamper/test/ObjectDeleterTest.java index 26a66f37..b53c55da 100644 --- a/src/test/java/org/wickedsource/docxstamper/util/ObjectDeleterTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/ObjectDeleterTest.java @@ -1,4 +1,4 @@ -package org.wickedsource.docxstamper.util; +package org.wickedsource.docxstamper.test; import org.docx4j.openpackaging.exceptions.Docx4JException; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; @@ -8,14 +8,17 @@ import org.docx4j.wml.Tr; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.utils.IOStreams; +import org.wickedsource.docxstamper.util.DocumentUtil; +import org.wickedsource.docxstamper.util.ObjectDeleter; +import org.wickedsource.docxstamper.util.ParagraphWrapper; +import pro.verron.docxstamper.test.utils.IOStreams; import java.io.IOException; import java.nio.file.Path; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.wickedsource.docxstamper.DefaultTests.getResource; +import static org.wickedsource.docxstamper.test.DefaultTests.getResource; import static org.wickedsource.docxstamper.util.DocumentUtil.getParagraphsFromObject; import static org.wickedsource.docxstamper.util.DocumentUtil.getTableFromObject; diff --git a/src/test/java/org/wickedsource/docxstamper/replace/ParagraphWrapperTest.java b/src/test/java/org/wickedsource/docxstamper/test/ParagraphWrapperTest.java similarity index 95% rename from src/test/java/org/wickedsource/docxstamper/replace/ParagraphWrapperTest.java rename to src/test/java/org/wickedsource/docxstamper/test/ParagraphWrapperTest.java index 62ee7670..011469ed 100644 --- a/src/test/java/org/wickedsource/docxstamper/replace/ParagraphWrapperTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/ParagraphWrapperTest.java @@ -1,4 +1,4 @@ -package org.wickedsource.docxstamper.replace; +package org.wickedsource.docxstamper.test; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/wickedsource/docxstamper/RepeatDocPartBadExpressionTest.java b/src/test/java/org/wickedsource/docxstamper/test/RepeatDocPartBadExpressionTest.java similarity index 78% rename from src/test/java/org/wickedsource/docxstamper/RepeatDocPartBadExpressionTest.java rename to src/test/java/org/wickedsource/docxstamper/test/RepeatDocPartBadExpressionTest.java index e6b52752..5befcd37 100644 --- a/src/test/java/org/wickedsource/docxstamper/RepeatDocPartBadExpressionTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/RepeatDocPartBadExpressionTest.java @@ -1,56 +1,57 @@ -package org.wickedsource.docxstamper; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; -import org.junit.platform.commons.logging.Logger; -import org.junit.platform.commons.logging.LoggerFactory; -import org.wickedsource.docxstamper.api.DocxStamperException; -import pro.verron.docxstamper.utils.TestDocxStamper; -import pro.verron.docxstamper.utils.context.Contexts.Characters; -import pro.verron.docxstamper.utils.context.Contexts.Role; - -import java.nio.file.Path; -import java.util.Arrays; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.wickedsource.docxstamper.DefaultTests.getResource; - -/** - * @author Jenei Attila - * @author Joseph Verrron - * @version ${version} - * @since 1.6.6 - */ -public class RepeatDocPartBadExpressionTest { - private static final Logger logger = - LoggerFactory.getLogger(RepeatDocPartBadExpressionTest.class); - - @Test - @Timeout(10) // in the case of pipe lock because of unknown exceptions - public void testBadExpressionShouldNotBlockCallerThread() { - var template = getResource(Path.of("RepeatDocPartBadExpressionTest.docx")); - var context = new Characters( - List.of(new Role("Homer Simpson", "Dan Castellaneta"), - new Role("Marge Simpson", "Julie Kavner"), - new Role("Bart Simpson", "Nancy Cartwright"))); - var stamper = new TestDocxStamper<>(new DocxStamperConfiguration()); - - var exception = assertThrows( - DocxStamperException.class, - () -> stamper.stampAndLoadAndExtract(template, context)); - - String expectedErrorInfo = "someUnknownField"; - var findDirectInfo = exception.getMessage() - .contains(expectedErrorInfo); - var findSuppressedInfo = Arrays.stream(exception.getSuppressed()) - .map(Throwable::getMessage) - .anyMatch(s -> s.contains(expectedErrorInfo)); - - logger.info(exception, () -> "Here is the exception info dump:"); - String errorMessage = "Could not find the expected '%s' information" - .formatted(expectedErrorInfo); - assertTrue(findDirectInfo || findSuppressedInfo, errorMessage); - } -} +package org.wickedsource.docxstamper.test; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.wickedsource.docxstamper.DocxStamperConfiguration; +import org.wickedsource.docxstamper.api.DocxStamperException; +import pro.verron.docxstamper.test.utils.TestDocxStamper; +import pro.verron.docxstamper.test.utils.context.Contexts.Characters; +import pro.verron.docxstamper.test.utils.context.Contexts.Role; + +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.wickedsource.docxstamper.test.DefaultTests.getResource; + +/** + * @author Jenei Attila + * @author Joseph Verrron + * @version ${version} + * @since 1.6.6 + */ +public class RepeatDocPartBadExpressionTest { + private static final Logger logger = + LoggerFactory.getLogger(RepeatDocPartBadExpressionTest.class); + + @Test + @Timeout(10) // in the case of pipe lock because of unknown exceptions + public void testBadExpressionShouldNotBlockCallerThread() { + var template = getResource(Path.of("RepeatDocPartBadExpressionTest.docx")); + var context = new Characters( + List.of(new Role("Homer Simpson", "Dan Castellaneta"), + new Role("Marge Simpson", "Julie Kavner"), + new Role("Bart Simpson", "Nancy Cartwright"))); + var stamper = new TestDocxStamper<>(new DocxStamperConfiguration()); + + var exception = assertThrows( + DocxStamperException.class, + () -> stamper.stampAndLoadAndExtract(template, context)); + + String expectedErrorInfo = "someUnknownField"; + var findDirectInfo = exception.getMessage() + .contains(expectedErrorInfo); + var findSuppressedInfo = Arrays.stream(exception.getSuppressed()) + .map(Throwable::getMessage) + .anyMatch(s -> s.contains(expectedErrorInfo)); + + logger.info("Here is the exception info dump:", exception); + String errorMessage = "Could not find the expected '%s' information" + .formatted(expectedErrorInfo); + assertTrue(findDirectInfo || findSuppressedInfo, errorMessage); + } +} diff --git a/src/test/java/org/wickedsource/docxstamper/util/RunUtilTest.java b/src/test/java/org/wickedsource/docxstamper/test/RunUtilTest.java similarity index 89% rename from src/test/java/org/wickedsource/docxstamper/util/RunUtilTest.java rename to src/test/java/org/wickedsource/docxstamper/test/RunUtilTest.java index 53c760b5..54862833 100644 --- a/src/test/java/org/wickedsource/docxstamper/util/RunUtilTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/RunUtilTest.java @@ -1,4 +1,4 @@ -package org.wickedsource.docxstamper.util; +package org.wickedsource.docxstamper.test; import org.docx4j.openpackaging.exceptions.Docx4JException; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; @@ -6,13 +6,14 @@ import org.docx4j.wml.R; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.utils.IOStreams; +import org.wickedsource.docxstamper.util.RunUtil; +import pro.verron.docxstamper.test.utils.IOStreams; import java.io.IOException; import java.nio.file.Path; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.wickedsource.docxstamper.DefaultTests.getResource; +import static org.wickedsource.docxstamper.test.DefaultTests.getResource; /** * @author Joseph Verron diff --git a/src/test/java/pro/verron/docxstamper/Functions.java b/src/test/java/pro/verron/docxstamper/test/Functions.java similarity index 95% rename from src/test/java/pro/verron/docxstamper/Functions.java rename to src/test/java/pro/verron/docxstamper/test/Functions.java index 2aeb3efa..d33e98e3 100644 --- a/src/test/java/pro/verron/docxstamper/Functions.java +++ b/src/test/java/pro/verron/docxstamper/test/Functions.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper; +package pro.verron.docxstamper.test; /** *

Functions class.

diff --git a/src/test/java/pro/verron/docxstamper/SpelInjectionTest.java b/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java similarity index 79% rename from src/test/java/pro/verron/docxstamper/SpelInjectionTest.java rename to src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java index d84da368..98998808 100644 --- a/src/test/java/pro/verron/docxstamper/SpelInjectionTest.java +++ b/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java @@ -1,17 +1,17 @@ -package pro.verron.docxstamper; +package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; import org.wickedsource.docxstamper.DocxStamperConfiguration; import org.wickedsource.docxstamper.api.DocxStamperException; -import pro.verron.docxstamper.utils.TestDocxStamper; -import pro.verron.docxstamper.utils.context.Contexts; +import pro.verron.docxstamper.test.utils.TestDocxStamper; +import pro.verron.docxstamper.test.utils.context.Contexts; import java.io.IOException; import java.nio.file.Path; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.wickedsource.docxstamper.DefaultTests.getResource; +import static org.wickedsource.docxstamper.test.DefaultTests.getResource; /** * @author Joseph Verron diff --git a/src/test/java/pro/verron/docxstamper/StampTableTest.java b/src/test/java/pro/verron/docxstamper/test/StampTableTest.java similarity index 90% rename from src/test/java/pro/verron/docxstamper/StampTableTest.java rename to src/test/java/pro/verron/docxstamper/test/StampTableTest.java index b21a353f..10464526 100644 --- a/src/test/java/pro/verron/docxstamper/StampTableTest.java +++ b/src/test/java/pro/verron/docxstamper/test/StampTableTest.java @@ -1,15 +1,15 @@ -package pro.verron.docxstamper; +package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; import org.wickedsource.docxstamper.DocxStamperConfiguration; -import pro.verron.docxstamper.utils.TestDocxStamper; -import pro.verron.docxstamper.utils.context.Contexts; +import pro.verron.docxstamper.test.utils.TestDocxStamper; +import pro.verron.docxstamper.test.utils.context.Contexts; import java.nio.file.Path; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.wickedsource.docxstamper.DefaultTests.getResource; +import static org.wickedsource.docxstamper.test.DefaultTests.getResource; /** * A test class that verifies that stampTable feature works correctly diff --git a/src/test/java/pro/verron/docxstamper/accessors/SimpleGetter.java b/src/test/java/pro/verron/docxstamper/test/accessors/SimpleGetter.java similarity index 97% rename from src/test/java/pro/verron/docxstamper/accessors/SimpleGetter.java rename to src/test/java/pro/verron/docxstamper/test/accessors/SimpleGetter.java index c1e721b9..0d171e12 100644 --- a/src/test/java/pro/verron/docxstamper/accessors/SimpleGetter.java +++ b/src/test/java/pro/verron/docxstamper/test/accessors/SimpleGetter.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.accessors; +package pro.verron.docxstamper.test.accessors; import org.springframework.expression.EvaluationContext; import org.springframework.expression.PropertyAccessor; diff --git a/src/test/java/pro/verron/docxstamper/commentProcessors/CustomCommentProcessor.java b/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java similarity index 97% rename from src/test/java/pro/verron/docxstamper/commentProcessors/CustomCommentProcessor.java rename to src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java index 22016604..62451772 100644 --- a/src/test/java/pro/verron/docxstamper/commentProcessors/CustomCommentProcessor.java +++ b/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.commentProcessors; +package pro.verron.docxstamper.test.commentProcessors; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.P; diff --git a/src/test/java/pro/verron/docxstamper/commentProcessors/ICustomCommentProcessor.java b/src/test/java/pro/verron/docxstamper/test/commentProcessors/ICustomCommentProcessor.java similarity index 86% rename from src/test/java/pro/verron/docxstamper/commentProcessors/ICustomCommentProcessor.java rename to src/test/java/pro/verron/docxstamper/test/commentProcessors/ICustomCommentProcessor.java index 4c5888ee..0232bcaa 100644 --- a/src/test/java/pro/verron/docxstamper/commentProcessors/ICustomCommentProcessor.java +++ b/src/test/java/pro/verron/docxstamper/test/commentProcessors/ICustomCommentProcessor.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.commentProcessors; +package pro.verron.docxstamper.test.commentProcessors; import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; diff --git a/src/test/java/pro/verron/docxstamper/resolver/CustomTypeResolver.java b/src/test/java/pro/verron/docxstamper/test/resolver/CustomTypeResolver.java similarity index 86% rename from src/test/java/pro/verron/docxstamper/resolver/CustomTypeResolver.java rename to src/test/java/pro/verron/docxstamper/test/resolver/CustomTypeResolver.java index 80a6bdad..8f064ce1 100644 --- a/src/test/java/pro/verron/docxstamper/resolver/CustomTypeResolver.java +++ b/src/test/java/pro/verron/docxstamper/test/resolver/CustomTypeResolver.java @@ -1,7 +1,7 @@ -package pro.verron.docxstamper.resolver; +package pro.verron.docxstamper.test.resolver; import pro.verron.docxstamper.api.StringResolver; -import pro.verron.docxstamper.utils.context.Contexts; +import pro.verron.docxstamper.test.utils.context.Contexts; /** *

CustomTypeResolver class.

diff --git a/src/test/java/pro/verron/docxstamper/utils/DocxCollector.java b/src/test/java/pro/verron/docxstamper/test/utils/DocxCollector.java similarity index 96% rename from src/test/java/pro/verron/docxstamper/utils/DocxCollector.java rename to src/test/java/pro/verron/docxstamper/test/utils/DocxCollector.java index 6a3e7b7c..a1f54882 100644 --- a/src/test/java/pro/verron/docxstamper/utils/DocxCollector.java +++ b/src/test/java/pro/verron/docxstamper/test/utils/DocxCollector.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.utils; +package pro.verron.docxstamper.test.utils; import org.docx4j.TraversalUtil; diff --git a/src/test/java/pro/verron/docxstamper/utils/IOStreams.java b/src/test/java/pro/verron/docxstamper/test/utils/IOStreams.java similarity index 97% rename from src/test/java/pro/verron/docxstamper/utils/IOStreams.java rename to src/test/java/pro/verron/docxstamper/test/utils/IOStreams.java index 7b795f7d..671c3882 100644 --- a/src/test/java/pro/verron/docxstamper/utils/IOStreams.java +++ b/src/test/java/pro/verron/docxstamper/test/utils/IOStreams.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.utils; +package pro.verron.docxstamper.test.utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/test/java/pro/verron/docxstamper/utils/RunCollector.java b/src/test/java/pro/verron/docxstamper/test/utils/RunCollector.java similarity index 93% rename from src/test/java/pro/verron/docxstamper/utils/RunCollector.java rename to src/test/java/pro/verron/docxstamper/test/utils/RunCollector.java index 2df176bf..46b72023 100644 --- a/src/test/java/pro/verron/docxstamper/utils/RunCollector.java +++ b/src/test/java/pro/verron/docxstamper/test/utils/RunCollector.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.utils; +package pro.verron.docxstamper.test.utils; import org.docx4j.utils.TraversalUtilVisitor; import org.docx4j.wml.R; diff --git a/src/test/java/pro/verron/docxstamper/utils/Stringifier.java b/src/test/java/pro/verron/docxstamper/test/utils/Stringifier.java similarity index 99% rename from src/test/java/pro/verron/docxstamper/utils/Stringifier.java rename to src/test/java/pro/verron/docxstamper/test/utils/Stringifier.java index 28b93a90..92011a5f 100644 --- a/src/test/java/pro/verron/docxstamper/utils/Stringifier.java +++ b/src/test/java/pro/verron/docxstamper/test/utils/Stringifier.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.utils; +package pro.verron.docxstamper.test.utils; import jakarta.xml.bind.JAXBElement; import org.docx4j.TextUtils; diff --git a/src/test/java/pro/verron/docxstamper/utils/TestDocxStamper.java b/src/test/java/pro/verron/docxstamper/test/utils/TestDocxStamper.java similarity index 98% rename from src/test/java/pro/verron/docxstamper/utils/TestDocxStamper.java rename to src/test/java/pro/verron/docxstamper/test/utils/TestDocxStamper.java index 5b462189..1d6fdde9 100644 --- a/src/test/java/pro/verron/docxstamper/utils/TestDocxStamper.java +++ b/src/test/java/pro/verron/docxstamper/test/utils/TestDocxStamper.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.utils; +package pro.verron.docxstamper.test.utils; import org.docx4j.TraversalUtil; import org.docx4j.openpackaging.exceptions.Docx4JException; diff --git a/src/test/java/pro/verron/docxstamper/utils/ThrowingSupplier.java b/src/test/java/pro/verron/docxstamper/test/utils/ThrowingSupplier.java similarity index 92% rename from src/test/java/pro/verron/docxstamper/utils/ThrowingSupplier.java rename to src/test/java/pro/verron/docxstamper/test/utils/ThrowingSupplier.java index 9ca5d00b..42453655 100644 --- a/src/test/java/pro/verron/docxstamper/utils/ThrowingSupplier.java +++ b/src/test/java/pro/verron/docxstamper/test/utils/ThrowingSupplier.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.utils; +package pro.verron.docxstamper.test.utils; import java.util.function.Supplier; diff --git a/src/test/java/pro/verron/docxstamper/utils/context/Contexts.java b/src/test/java/pro/verron/docxstamper/test/utils/context/Contexts.java similarity index 96% rename from src/test/java/pro/verron/docxstamper/utils/context/Contexts.java rename to src/test/java/pro/verron/docxstamper/test/utils/context/Contexts.java index f3f3842f..f38ab75e 100644 --- a/src/test/java/pro/verron/docxstamper/utils/context/Contexts.java +++ b/src/test/java/pro/verron/docxstamper/test/utils/context/Contexts.java @@ -1,7 +1,7 @@ -package pro.verron.docxstamper.utils.context; +package pro.verron.docxstamper.test.utils.context; import org.wickedsource.docxstamper.processor.table.StampTable; -import org.wickedsource.docxstamper.replace.typeresolver.image.Image; +import pro.verron.docxstamper.api.Image; import java.util.*; @@ -62,7 +62,7 @@ record Names(List names) {} * * @param character a {@link java.lang.String} object * @param danCastellaneta a {@link java.lang.String} object - * @return a {@link pro.verron.docxstamper.utils.context.Contexts.Role} object + * @return a {@link pro.verron.docxstamper.test.utils.context.Contexts.Role} object * @since 1.6.6 */ public static Role role(String character, String danCastellaneta) { @@ -72,8 +72,8 @@ public static Role role(String character, String danCastellaneta) { /** *

roles.

* - * @param roles a {@link pro.verron.docxstamper.utils.context.Contexts.Role} object - * @return a {@link pro.verron.docxstamper.utils.context.Contexts.Characters} object + * @param roles a {@link pro.verron.docxstamper.test.utils.context.Contexts.Role} object + * @return a {@link pro.verron.docxstamper.test.utils.context.Contexts.Characters} object * @since 1.6.6 */ public static Characters roles(Role... roles) { @@ -105,7 +105,7 @@ public static HashMap subDocPartContext() { /** *

schoolContext.

* - * @return a {@link pro.verron.docxstamper.utils.context.Contexts.SchoolContext} object + * @return a {@link pro.verron.docxstamper.test.utils.context.Contexts.SchoolContext} object * @since 1.6.6 */ public static SchoolContext schoolContext() { @@ -177,7 +177,7 @@ public static Map coupleContext() { /** *

nowContext.

* - * @return a {@link pro.verron.docxstamper.utils.context.Contexts.DateContext} object + * @return a {@link pro.verron.docxstamper.test.utils.context.Contexts.DateContext} object * @since 1.6.6 */ public static DateContext nowContext() { @@ -205,7 +205,7 @@ public static HashMap mapAndReflectiveContext() { /** *

nullishContext.

* - * @return a {@link pro.verron.docxstamper.utils.context.Contexts.NullishContext} object + * @return a {@link pro.verron.docxstamper.test.utils.context.Contexts.NullishContext} object * @since 1.6.6 */ public static NullishContext nullishContext() { diff --git a/src/test/java/pro/verron/docxstamper/utils/context/NullishContext.java b/src/test/java/pro/verron/docxstamper/test/utils/context/NullishContext.java similarity index 85% rename from src/test/java/pro/verron/docxstamper/utils/context/NullishContext.java rename to src/test/java/pro/verron/docxstamper/test/utils/context/NullishContext.java index bfdc0bd7..6b70d5eb 100644 --- a/src/test/java/pro/verron/docxstamper/utils/context/NullishContext.java +++ b/src/test/java/pro/verron/docxstamper/test/utils/context/NullishContext.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.utils.context; +package pro.verron.docxstamper.test.utils.context; import java.util.Objects; @@ -25,9 +25,9 @@ public NullishContext() { *

Constructor for NullishContext.

* * @param fullish_value a {@link java.lang.String} object - * @param fullish a {@link pro.verron.docxstamper.utils.context.SubContext} object + * @param fullish a {@link pro.verron.docxstamper.test.utils.context.SubContext} object * @param nullish_value a {@link java.lang.String} object - * @param nullish a {@link pro.verron.docxstamper.utils.context.SubContext} object + * @param nullish a {@link pro.verron.docxstamper.test.utils.context.SubContext} object */ public NullishContext( String fullish_value, @@ -62,7 +62,7 @@ public void setFullish_value(String fullish_value) { /** *

Getter for the field fullish.

* - * @return a {@link pro.verron.docxstamper.utils.context.SubContext} object + * @return a {@link pro.verron.docxstamper.test.utils.context.SubContext} object */ public SubContext getFullish() { return fullish; @@ -71,7 +71,7 @@ public SubContext getFullish() { /** *

Setter for the field fullish.

* - * @param fullish a {@link pro.verron.docxstamper.utils.context.SubContext} object + * @param fullish a {@link pro.verron.docxstamper.test.utils.context.SubContext} object */ public void setFullish(SubContext fullish) { this.fullish = fullish; @@ -98,7 +98,7 @@ public void setNullish_value(String nullish_value) { /** *

Getter for the field nullish.

* - * @return a {@link pro.verron.docxstamper.utils.context.SubContext} object + * @return a {@link pro.verron.docxstamper.test.utils.context.SubContext} object */ public SubContext getNullish() { return nullish; @@ -107,7 +107,7 @@ public SubContext getNullish() { /** *

Setter for the field nullish.

* - * @param nullish a {@link pro.verron.docxstamper.utils.context.SubContext} object + * @param nullish a {@link pro.verron.docxstamper.test.utils.context.SubContext} object */ public void setNullish(SubContext nullish) { this.nullish = nullish; diff --git a/src/test/java/pro/verron/docxstamper/utils/context/SubContext.java b/src/test/java/pro/verron/docxstamper/test/utils/context/SubContext.java similarity index 97% rename from src/test/java/pro/verron/docxstamper/utils/context/SubContext.java rename to src/test/java/pro/verron/docxstamper/test/utils/context/SubContext.java index f03966aa..a89098c8 100644 --- a/src/test/java/pro/verron/docxstamper/utils/context/SubContext.java +++ b/src/test/java/pro/verron/docxstamper/test/utils/context/SubContext.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.utils.context; +package pro.verron.docxstamper.test.utils.context; import java.util.List; import java.util.Objects; From 1362bc0f9044c1764ca61f3f888f8c16022e3fc5 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 14 Mar 2024 16:13:55 +0100 Subject: [PATCH 014/134] Add Placeholder interface and rename related classes Introduced a new Placeholder interface in the API package to enhance code modularity and readability. Renamed existing classes related to "Expression" to "Placeholder" for consistency. Also deprecated the original Image class in favor of a new Image class in the API package. Updated module information to reflect these changes. --- README.adoc | 28 +++++++++---------- src/main/java/module-info.java | 8 ++++-- .../docxstamper/CommentProcessorBuilder.java | 2 +- .../wickedsource/docxstamper/DocxStamper.java | 2 +- .../docxstamper/el/ExpressionResolver.java | 8 +++--- .../processor/BaseCommentProcessor.java | 2 +- .../processor/CommentProcessorFactory.java | 2 +- .../displayif/DisplayIfProcessor.java | 2 +- .../repeat/ParagraphRepeatProcessor.java | 2 +- .../ParagraphResolverDocumentWalker.java | 2 +- .../repeat/RepeatDocPartProcessor.java | 2 +- .../processor/repeat/RepeatProcessor.java | 2 +- .../ReplaceWithProcessor.java | 2 +- .../processor/table/TableResolver.java | 2 +- .../replace/typeresolver/image/Image.java | 1 + .../docxstamper/util/CommentUtil.java | 6 ++-- .../docxstamper/util/ParagraphWrapper.java | 8 +++--- .../docxstamper/api/ObjectResolver.java | 9 +++--- .../verron/docxstamper/api/Placeholder.java | 7 +++++ ...xpression.java => DefaultPlaceholder.java} | 9 ++++-- .../verron/docxstamper/core/Expressions.java | 15 +++++----- .../core/ObjectResolverRegistry.java | 7 +++-- .../core}/PlaceholderReplacer.java | 18 ++++++------ .../core/expression/ExpressionFinder.java | 9 +++--- .../resolver/Null2PlaceholderResolver.java | 6 ++-- src/test/java/module-info.java | 1 + ...a => FailOnUnresolvedPlaceholderTest.java} | 2 +- ...lderReplacementInHeaderAndFooterTest.java} | 2 +- ...laceholderReplacementInTextBoxesTest.java} | 2 +- ...a => RepeatDocPartBadPlaceholderTest.java} | 4 +-- .../CustomCommentProcessor.java | 4 +-- 31 files changed, 95 insertions(+), 81 deletions(-) create mode 100644 src/main/java/pro/verron/docxstamper/api/Placeholder.java rename src/main/java/pro/verron/docxstamper/core/{Expression.java => DefaultPlaceholder.java} (75%) rename src/main/java/{org/wickedsource/docxstamper/replace => pro/verron/docxstamper/core}/PlaceholderReplacer.java (92%) rename src/test/java/org/wickedsource/docxstamper/test/{FailOnUnresolvedExpressionTest.java => FailOnUnresolvedPlaceholderTest.java} (97%) rename src/test/java/org/wickedsource/docxstamper/test/{ExpressionReplacementInHeaderAndFooterTest.java => PlaceholderReplacementInHeaderAndFooterTest.java} (98%) rename src/test/java/org/wickedsource/docxstamper/test/{ExpressionReplacementInTextBoxesTest.java => PlaceholderReplacementInTextBoxesTest.java} (95%) rename src/test/java/org/wickedsource/docxstamper/test/{RepeatDocPartBadExpressionTest.java => RepeatDocPartBadPlaceholderTest.java} (94%) diff --git a/README.adoc b/README.adoc index 0ce9ac3e..8264db13 100644 --- a/README.adoc +++ b/README.adoc @@ -36,25 +36,25 @@ class Example { == Replacing Expressions in a .docx Template The main feature of docx-stamper is *replacement of expressions* within the text of the template document. -Simply add expressions like `${person.name}` or `${person.name.equals("Homer") ? "Duff" : "Budweiser"}` in the text of your .docx template and provide a context object against which the expression can be resolved. docx-stamper will try to keep the original formatting of the text in the template intact. +Simply add expressions like `${person.name}` or `${person.name.equals("Homer") ? "Duff" : "Budweiser"}` in the text of your .docx template and provide a context object against which the placeholder can be resolved. docx-stamper will try to keep the original formatting of the text in the template intact. You can use the full feature set of http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html[Spring Expression Language] (SpEL). -The value that an expression resolves to may be of the following types: +The value that an placeholder resolves to may be of the following types: .Type that can be resolved to an element in the .docx document [cols=">1,3"] |=== -| Type of expression value | Effect -| `java.lang.Object` | The expression is replaced by the String representation of the object (`String.valueOf()`). -| `java.lang.String` | The expression is replaced with the String value. -| `java.util.Date` | The expression is replaced by a formatted Date string (by default "dd.MM.yyyy"). You can change the format string by registering your own `link:{repo}/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/DateResolver.java[DateResolver]`. -| `java.time.LocalDate` | The expression is replaced by a formatted Date string (by default DateTimeFormatter.ISO_LOCAL_DATE). You can change the format string by registering your own. -| `java.time.LocalDateTime` | The expression is replaced by a formatted Date string (by default DateTimeFormatter.ISO_LOCAL_DATE_TIME). You can change the format string by registering your own. -| `java.time.LocalTime` | The expression is replaced by a formatted Date string (by default DateTimeFormatter.ISO_LOCAL_TIME). You can change the format string by registering your own. -| `link:{repo}/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java[org.wickedsource...Image]` |The expression is replaced with an inline image. +| Type of placeholder value | Effect +| `java.lang.Object` | The placeholder is replaced by the String representation of the object (`String.valueOf()`). +| `java.lang.String` | The placeholder is replaced with the String value. +| `java.util.Date` | The placeholder is replaced by a formatted Date string (by default "dd.MM.yyyy"). You can change the format string by registering your own `link:{repo}/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/DateResolver.java[DateResolver]`. +| `java.time.LocalDate` | The placeholder is replaced by a formatted Date string (by default DateTimeFormatter.ISO_LOCAL_DATE). You can change the format string by registering your own. +| `java.time.LocalDateTime` | The placeholder is replaced by a formatted Date string (by default DateTimeFormatter.ISO_LOCAL_DATE_TIME). You can change the format string by registering your own. +| `java.time.LocalTime` | The placeholder is replaced by a formatted Date string (by default DateTimeFormatter.ISO_LOCAL_TIME). You can change the format string by registering your own. +| `link:{repo}/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java[org.wickedsource...Image]` |The placeholder is replaced with an inline image. |=== -If an expression cannot be resolved successfully, it will be skipped (meaning the expression stays in the document as it was in the template). +If an placeholder cannot be resolved successfully, it will be skipped (meaning the placeholder stays in the document as it was in the template). To support more than the above types, you can implement your own `link:{repo}/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java[ObjectResolver]`. To register your it with docx-stamper, use the following code: @@ -96,7 +96,7 @@ class Main { == Adding custom functions to the Expression Language -If you want to create custom functions (for different number formats or different date formats, for example), you can register functions which can then be used in the expression language. +If you want to create custom functions (for different number formats or different date formats, for example), you can register functions which can then be used in the placeholder language. The following code, for example, adds a function `toUppercase(String)` which can be used within the .docx document to uppercase a String: @@ -130,7 +130,7 @@ By default, you can use the following expressions in comments: |`displayTableIf(boolean)` | The whole table surrounding the commented paragraph is only displayed in the resulting .docx document if the boolean condition resolves to `true`. |`repeatTableRow(List<Object>)` | The table row surrounding the commented paragraph is copied once for each object in the passed-in list. Expressions found in the cells of the table row are evaluated against the object from the list. |`repeatDocPart(List<Object>)` | Repeat the part of the document surrounded by the comment. The document part is copied once for each object in the passed-in list. Expressions found in the elements of the document part are evaluated against the object from the list. Can be used instead of repeatTableRow and repeatParagraph if you want to repeat more than table rows and paragraphs. -|`replaceWordWith(expression)` | Replaces the commented word (must be a single word) with the value of the given expression. +|`replaceWordWith(expression)` | Replaces the commented word (must be a single word) with the value of the given placeholder. |`resolveTable(StampTable)` | Replace a table (that must have one column and two rows) with the values given by the StampTable. The StampTable contains a list of headers for columns, and a 2-level list of rows containing values for each column. |=== @@ -171,7 +171,7 @@ The expression will be evaluated as it would be in a comment. == Error Handling -By default, DocxStamper fails with an UnresolvedExpressionException if an expression within the document or within the comments cannot be resolved successfully. +By default, DocxStamper fails with an UnresolvedExpressionException if an placeholder within the document or within the comments cannot be resolved successfully. If you want to change this behavior, you can do the following: [source,java] diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index f2a7a96e..b7c703cc 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,14 +1,16 @@ module pro.verron.opcstamper { - exports org.wickedsource.docxstamper.el; exports pro.verron.docxstamper.api; exports pro.verron.docxstamper.preset.resolver; - exports org.wickedsource.docxstamper.processor.table; + exports org.wickedsource.docxstamper; + exports org.wickedsource.docxstamper.el; + exports org.wickedsource.docxstamper.processor.table; exports org.wickedsource.docxstamper.util; exports org.wickedsource.docxstamper.api.commentprocessor; exports org.wickedsource.docxstamper.processor; - exports org.wickedsource.docxstamper.replace; exports org.wickedsource.docxstamper.api; + exports pro.verron.docxstamper.core; + requires org.apache.commons.io; requires org.docx4j.core; requires spring.expression; diff --git a/src/main/java/org/wickedsource/docxstamper/CommentProcessorBuilder.java b/src/main/java/org/wickedsource/docxstamper/CommentProcessorBuilder.java index 7663b0e8..f1a454d0 100644 --- a/src/main/java/org/wickedsource/docxstamper/CommentProcessorBuilder.java +++ b/src/main/java/org/wickedsource/docxstamper/CommentProcessorBuilder.java @@ -1,7 +1,7 @@ package org.wickedsource.docxstamper; import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; -import org.wickedsource.docxstamper.replace.PlaceholderReplacer; +import pro.verron.docxstamper.core.PlaceholderReplacer; /** * Factory interface for creating {@link ICommentProcessor} instances. diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java index fda7f4af..6a26aaf0 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java @@ -12,12 +12,12 @@ import org.wickedsource.docxstamper.el.ExpressionResolver; import org.wickedsource.docxstamper.el.StandardMethodResolver; import org.wickedsource.docxstamper.processor.CommentProcessorRegistry; -import org.wickedsource.docxstamper.replace.PlaceholderReplacer; import pro.verron.docxstamper.OpcStamper; import pro.verron.docxstamper.StamperFactory; import pro.verron.docxstamper.api.ObjectResolver; import pro.verron.docxstamper.core.Expressions; import pro.verron.docxstamper.core.ObjectResolverRegistry; +import pro.verron.docxstamper.core.PlaceholderReplacer; import java.io.InputStream; import java.io.OutputStream; diff --git a/src/main/java/org/wickedsource/docxstamper/el/ExpressionResolver.java b/src/main/java/org/wickedsource/docxstamper/el/ExpressionResolver.java index 69ecb9b1..92a79bb2 100644 --- a/src/main/java/org/wickedsource/docxstamper/el/ExpressionResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/el/ExpressionResolver.java @@ -4,7 +4,7 @@ import org.springframework.expression.spel.SpelParserConfiguration; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; -import pro.verron.docxstamper.core.Expression; +import pro.verron.docxstamper.api.Placeholder; /** * Resolves expressions against a given context object. Expressions can be either SpEL expressions or simple property @@ -37,13 +37,13 @@ public ExpressionResolver( /** * Resolves the given expression against the provided context object. * - * @param expression the expression to resolve. + * @param placeholder the expression to resolve. * @param contextRoot the context object against which to resolve the expression. * @return the resolved value of the expression. */ - public Object resolve(Expression expression, Object contextRoot) { + public Object resolve(Placeholder placeholder, Object contextRoot) { evaluationContext.setRootObject(contextRoot); - return parser.parseExpression(expression.inner()) + return parser.parseExpression(placeholder.content()) .getValue(evaluationContext); } } diff --git a/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java index 2ac7b990..488e0fb9 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java @@ -5,8 +5,8 @@ import org.docx4j.wml.R; import org.wickedsource.docxstamper.DocxStamper; import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; -import org.wickedsource.docxstamper.replace.PlaceholderReplacer; import org.wickedsource.docxstamper.util.CommentWrapper; +import pro.verron.docxstamper.core.PlaceholderReplacer; import java.util.Objects; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorFactory.java b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorFactory.java index 656da102..84b684e2 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorFactory.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorFactory.java @@ -10,8 +10,8 @@ import org.wickedsource.docxstamper.processor.repeat.RepeatProcessor; import org.wickedsource.docxstamper.processor.replaceExpression.ReplaceWithProcessor; import org.wickedsource.docxstamper.processor.table.TableResolver; -import org.wickedsource.docxstamper.replace.PlaceholderReplacer; import pro.verron.docxstamper.OpcStamper; +import pro.verron.docxstamper.core.PlaceholderReplacer; /** * Factory class to create the correct comment processor for a given comment. diff --git a/src/main/java/org/wickedsource/docxstamper/processor/displayif/DisplayIfProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/displayif/DisplayIfProcessor.java index 16314c79..390c37db 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/displayif/DisplayIfProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/displayif/DisplayIfProcessor.java @@ -8,8 +8,8 @@ import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.processor.CommentProcessingException; -import org.wickedsource.docxstamper.replace.PlaceholderReplacer; import org.wickedsource.docxstamper.util.ObjectDeleter; +import pro.verron.docxstamper.core.PlaceholderReplacer; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java index 637310e5..ea22ddc4 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java @@ -6,8 +6,8 @@ import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; -import org.wickedsource.docxstamper.replace.PlaceholderReplacer; import org.wickedsource.docxstamper.util.*; +import pro.verron.docxstamper.core.PlaceholderReplacer; import java.math.BigInteger; import java.util.*; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java index 521856d1..8ed18708 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java @@ -3,9 +3,9 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.P; import org.docx4j.wml.Tr; -import org.wickedsource.docxstamper.replace.PlaceholderReplacer; import org.wickedsource.docxstamper.util.ParagraphWrapper; import org.wickedsource.docxstamper.util.walk.BaseDocumentWalker; +import pro.verron.docxstamper.core.PlaceholderReplacer; /** * Walks through a document and replaces expressions with values from the given diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java index 8a0d3136..45557851 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java @@ -8,12 +8,12 @@ import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; -import org.wickedsource.docxstamper.replace.PlaceholderReplacer; import org.wickedsource.docxstamper.util.CommentWrapper; import org.wickedsource.docxstamper.util.DocumentUtil; import org.wickedsource.docxstamper.util.ParagraphUtil; import org.wickedsource.docxstamper.util.SectionUtil; import pro.verron.docxstamper.OpcStamper; +import pro.verron.docxstamper.core.PlaceholderReplacer; import java.io.IOException; import java.io.OutputStream; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java index fd72ed7c..48d126bb 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java @@ -6,9 +6,9 @@ import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.processor.CommentProcessingException; -import org.wickedsource.docxstamper.replace.PlaceholderReplacer; import org.wickedsource.docxstamper.util.CommentUtil; import org.wickedsource.docxstamper.util.CommentWrapper; +import pro.verron.docxstamper.core.PlaceholderReplacer; import java.math.BigInteger; import java.util.*; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/replaceExpression/ReplaceWithProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/replaceExpression/ReplaceWithProcessor.java index 058f2d30..05873f95 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/replaceExpression/ReplaceWithProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/replaceExpression/ReplaceWithProcessor.java @@ -5,8 +5,8 @@ import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; -import org.wickedsource.docxstamper.replace.PlaceholderReplacer; import org.wickedsource.docxstamper.util.RunUtil; +import pro.verron.docxstamper.core.PlaceholderReplacer; import java.util.List; import java.util.function.Function; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java b/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java index 4cedeead..3956c5c2 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java @@ -7,8 +7,8 @@ import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.processor.CommentProcessingException; -import org.wickedsource.docxstamper.replace.PlaceholderReplacer; import org.wickedsource.docxstamper.util.ParagraphUtil; +import pro.verron.docxstamper.core.PlaceholderReplacer; import java.util.Collections; import java.util.HashMap; diff --git a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java index a8071f06..2478ad46 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java @@ -11,6 +11,7 @@ * @version ${version} * @since 1.0.0 */ +@Deprecated(since = "1.6.8", forRemoval = true) public final class Image extends pro.verron.docxstamper.api.Image { diff --git a/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java b/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java index b8544e56..53210ad2 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java +++ b/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java @@ -14,7 +14,7 @@ import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.util.walk.BaseDocumentWalker; import org.wickedsource.docxstamper.util.walk.DocumentWalker; -import pro.verron.docxstamper.core.Expression; +import pro.verron.docxstamper.api.Placeholder; import pro.verron.docxstamper.core.Expressions; import java.math.BigInteger; @@ -130,7 +130,7 @@ public static Optional findComment( * and will be removed in the future. */ @Deprecated(since = "1.6.8", forRemoval = true) - public static Expression getCommentStringFor( + public static Placeholder getCommentStringFor( ContentAccessor object, WordprocessingMLPackage document ) { Comment comment = getCommentFor(object, document).orElseThrow(); @@ -190,7 +190,7 @@ public static Optional getCommentFor( * @param comment a {@link Comment} object * @return a {@link String} object */ - public static Expression getCommentString(Comment comment) { + public static Placeholder getCommentString(Comment comment) { StringBuilder builder = new StringBuilder(); for (Object commentChildObject : comment.getContent()) { if (commentChildObject instanceof P p) { diff --git a/src/main/java/org/wickedsource/docxstamper/util/ParagraphWrapper.java b/src/main/java/org/wickedsource/docxstamper/util/ParagraphWrapper.java index 190ecc67..16f25f3e 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/ParagraphWrapper.java +++ b/src/main/java/org/wickedsource/docxstamper/util/ParagraphWrapper.java @@ -2,7 +2,7 @@ import org.docx4j.wml.P; import org.docx4j.wml.R; -import pro.verron.docxstamper.core.Expression; +import pro.verron.docxstamper.api.Placeholder; import java.util.ArrayList; import java.util.List; @@ -75,12 +75,12 @@ private void addRun(R run, int index) { * the paragraph. * The replacement object must be a valid DOCX4J Object. * - * @param expression the expression to be replaced. + * @param placeholder the expression to be replaced. * @param replacement the object to replace the expression. */ - public void replace(Expression expression, R replacement) { + public void replace(Placeholder placeholder, R replacement) { String text = getText(); - String full = expression.expression(); + String full = placeholder.expression(); int matchStartIndex = text.indexOf(full); if (matchStartIndex == -1) { // nothing to replace diff --git a/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java b/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java index 04ce647a..f3fe0747 100644 --- a/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java +++ b/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java @@ -5,7 +5,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wickedsource.docxstamper.api.DocxStamperException; -import pro.verron.docxstamper.core.Expression; /** * The ObjectResolver interface provides a contract for resolving objects to create a run @@ -23,19 +22,19 @@ public interface ObjectResolver { * * @param document the {@link WordprocessingMLPackage} document in * which to resolve the expression - * @param expression the expression value to be replaced + * @param placeholder the expression value to be replaced * @param object the object to be used for resolving the expression * @return the resolved value for the expression * @throws DocxStamperException if no resolver is found for the object */ default R resolve( WordprocessingMLPackage document, - Expression expression, + Placeholder placeholder, Object object ) { - R resolution = resolve(document, expression.inner(), object); + R resolution = resolve(document, placeholder.content(), object); var msg = "Expression '{}' replaced by '{}' with resolver {}"; - Log.log.debug(msg, expression, resolution, this); + Log.log.debug(msg, placeholder, resolution, this); return resolution; } diff --git a/src/main/java/pro/verron/docxstamper/api/Placeholder.java b/src/main/java/pro/verron/docxstamper/api/Placeholder.java new file mode 100644 index 00000000..e376710b --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/api/Placeholder.java @@ -0,0 +1,7 @@ +package pro.verron.docxstamper.api; + +public interface Placeholder { + String content(); + + String expression(); +} diff --git a/src/main/java/pro/verron/docxstamper/core/Expression.java b/src/main/java/pro/verron/docxstamper/core/DefaultPlaceholder.java similarity index 75% rename from src/main/java/pro/verron/docxstamper/core/Expression.java rename to src/main/java/pro/verron/docxstamper/core/DefaultPlaceholder.java index 22105f4e..5a00fa96 100644 --- a/src/main/java/pro/verron/docxstamper/core/Expression.java +++ b/src/main/java/pro/verron/docxstamper/core/DefaultPlaceholder.java @@ -1,21 +1,24 @@ package pro.verron.docxstamper.core; +import pro.verron.docxstamper.api.Placeholder; import pro.verron.docxstamper.core.expression.Matcher; /** * Represents an expression with a configured Matcher. */ -public record Expression( +public record DefaultPlaceholder( Matcher matcher, String expression -) { +) + implements Placeholder { /** * Returns the inner part of the expression * by stripping the prefix and suffix. * * @return the inner part of the expression. */ - public String inner() { + @Override + public String content() { return matcher.match(expression) ? matcher.strip(expression) : expression; diff --git a/src/main/java/pro/verron/docxstamper/core/Expressions.java b/src/main/java/pro/verron/docxstamper/core/Expressions.java index 9d55c188..1d5b4931 100644 --- a/src/main/java/pro/verron/docxstamper/core/Expressions.java +++ b/src/main/java/pro/verron/docxstamper/core/Expressions.java @@ -2,6 +2,7 @@ import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.util.ParagraphWrapper; +import pro.verron.docxstamper.api.Placeholder; import pro.verron.docxstamper.core.expression.ExpressionFinder; import pro.verron.docxstamper.core.expression.Matcher; @@ -69,13 +70,13 @@ private Expressions() { * Finds variable expressions in a given text. * * @param text the text to search for variable expressions - * @return a list of found variable expressions as {@link Expression} objects + * @return a list of found variable expressions as {@link DefaultPlaceholder} objects */ - public static List findVariables(String text) { + public static List findVariables(String text) { return VAR_FINDER.find(text); } - public static List findVariables(ParagraphWrapper paragraph) { + public static List findVariables(ParagraphWrapper paragraph) { return findVariables(paragraph.getText()); } @@ -83,14 +84,14 @@ public static List findVariables(ParagraphWrapper paragraph) { * Finds processors expressions in a given text. * * @param text the text to search for processor expressions - * @return a list of found processor expressions as {@link Expression} + * @return a list of found processor expressions as {@link DefaultPlaceholder} * objects */ - public static List findProcessors(String text) { + public static List findProcessors(String text) { return PROC_FINDER.find(text); } - public static Expression raw(String text) { - return new Expression(RAW_MATCHER, text); + public static Placeholder raw(String text) { + return new DefaultPlaceholder(RAW_MATCHER, text); } } diff --git a/src/main/java/pro/verron/docxstamper/core/ObjectResolverRegistry.java b/src/main/java/pro/verron/docxstamper/core/ObjectResolverRegistry.java index 8fa22eaa..b26a4211 100644 --- a/src/main/java/pro/verron/docxstamper/core/ObjectResolverRegistry.java +++ b/src/main/java/pro/verron/docxstamper/core/ObjectResolverRegistry.java @@ -4,6 +4,7 @@ import org.docx4j.wml.R; import org.wickedsource.docxstamper.api.DocxStamperException; import pro.verron.docxstamper.api.ObjectResolver; +import pro.verron.docxstamper.api.Placeholder; import java.util.ArrayList; import java.util.List; @@ -32,19 +33,19 @@ public ObjectResolverRegistry(List resolvers) { * Resolves the expression in the given document with the provided object. * * @param document the WordprocessingMLPackage document in which to resolve the placeholder - * @param expression the expression value to be replaced + * @param placeholder the expression value to be replaced * @param object the object to be used for resolving the expression * @return the resolved value for the expression * @throws DocxStamperException if no resolver is found for the object */ public R resolve( WordprocessingMLPackage document, - Expression expression, + Placeholder placeholder, Object object ) { for (ObjectResolver resolver : resolvers) if (resolver.canResolve(object)) - return resolver.resolve(document, expression, object); + return resolver.resolve(document, placeholder, object); throw new DocxStamperException("No resolver for %s".formatted(object)); } } diff --git a/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java similarity index 92% rename from src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java rename to src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java index 89c66330..67980118 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java +++ b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java @@ -1,4 +1,4 @@ -package org.wickedsource.docxstamper.replace; +package pro.verron.docxstamper.core; import org.docx4j.jaxb.Context; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; @@ -14,9 +14,7 @@ import org.wickedsource.docxstamper.util.ParagraphWrapper; import org.wickedsource.docxstamper.util.RunUtil; import org.wickedsource.docxstamper.util.walk.BaseCoordinatesWalker; -import pro.verron.docxstamper.core.Expression; -import pro.verron.docxstamper.core.Expressions; -import pro.verron.docxstamper.core.ObjectResolverRegistry; +import pro.verron.docxstamper.api.Placeholder; /** * Replaces expressions in a document with the values provided by the {@link ExpressionResolver}. @@ -35,7 +33,7 @@ public class PlaceholderReplacer { private final boolean leaveEmptyOnExpressionError; private final boolean replaceUnresolvedExpressions; private final String unresolvedExpressionsDefaultValue; - private final Expression lineBreakPlaceholder; + private final Placeholder lineBreakPlaceholder; /** *

Constructor for PlaceholderReplacer.

@@ -51,7 +49,7 @@ public class PlaceholderReplacer { * that cannot be resolved will * be by replaced by an * empty string. - * @param lineBreakExpression if set to a non-null value, + * @param linebreakPlaceholder if set to a non-null value, * all occurrences of this placeholder will be * replaced with a line break. */ @@ -62,7 +60,7 @@ public PlaceholderReplacer( boolean replaceUnresolvedExpressions, String unresolvedExpressionsDefaultValue, boolean leaveEmptyOnExpressionError, - Expression lineBreakExpression + Placeholder linebreakPlaceholder ) { this.registry = registry; this.resolver = resolver; @@ -70,7 +68,7 @@ public PlaceholderReplacer( this.replaceUnresolvedExpressions = replaceUnresolvedExpressions; this.unresolvedExpressionsDefaultValue = unresolvedExpressionsDefaultValue; this.leaveEmptyOnExpressionError = leaveEmptyOnExpressionError; - this.lineBreakPlaceholder = lineBreakExpression; + this.lineBreakPlaceholder = linebreakPlaceholder; } /** @@ -149,7 +147,7 @@ public void resolveExpressionsForParagraph( // TODO: Remove this intermediate method - private Expression lineBreakPlaceholder() { + private Placeholder lineBreakPlaceholder() { return lineBreakPlaceholder; } @@ -158,7 +156,7 @@ private void replaceLineBreaks(ParagraphWrapper paragraph) { .createBr(); R run = RunUtil.create(lineBreak); while (paragraph.getText() - .contains(lineBreakPlaceholder().inner())) { + .contains(lineBreakPlaceholder().expression())) { paragraph.replace(lineBreakPlaceholder(), run); } } diff --git a/src/main/java/pro/verron/docxstamper/core/expression/ExpressionFinder.java b/src/main/java/pro/verron/docxstamper/core/expression/ExpressionFinder.java index a948eeb8..7b33ab81 100644 --- a/src/main/java/pro/verron/docxstamper/core/expression/ExpressionFinder.java +++ b/src/main/java/pro/verron/docxstamper/core/expression/ExpressionFinder.java @@ -1,6 +1,7 @@ package pro.verron.docxstamper.core.expression; -import pro.verron.docxstamper.core.Expression; +import pro.verron.docxstamper.api.Placeholder; +import pro.verron.docxstamper.core.DefaultPlaceholder; import java.util.ArrayList; import java.util.List; @@ -25,15 +26,15 @@ public record ExpressionFinder( * @param text the text to search for expressions * @return a list of found expressions */ - public List find(String text) { + public List find(String text) { if (text.isEmpty()) return emptyList(); var matcher = pattern.matcher(text); int index = 0; - List matches = new ArrayList<>(); + List matches = new ArrayList<>(); while (matcher.find(index)) { String match = matcher.group(); - matches.add(new Expression(this.matcher, match)); + matches.add(new DefaultPlaceholder(this.matcher, match)); index = matcher.end(); } return matches; diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/Null2PlaceholderResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/Null2PlaceholderResolver.java index 68036249..973148c6 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/Null2PlaceholderResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/Null2PlaceholderResolver.java @@ -5,7 +5,7 @@ import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.util.RunUtil; import pro.verron.docxstamper.api.ObjectResolver; -import pro.verron.docxstamper.core.Expression; +import pro.verron.docxstamper.api.Placeholder; /** * The {@link Null2PlaceholderResolver} class is an implementation of the ObjectResolver interface. @@ -30,10 +30,10 @@ public boolean canResolve(Object object) { @Override public R resolve( WordprocessingMLPackage document, - Expression expression, + Placeholder placeholder, Object object ) { - return RunUtil.create(expression.expression()); + return RunUtil.create(placeholder.expression()); } @Override diff --git a/src/test/java/module-info.java b/src/test/java/module-info.java index e5159812..9e491bb6 100644 --- a/src/test/java/module-info.java +++ b/src/test/java/module-info.java @@ -8,6 +8,7 @@ requires org.docx4j.core; requires org.slf4j; requires jakarta.xml.bind; + requires spring.core; opens pro.verron.docxstamper.test to spring.expression, org.junit.platform.commons; diff --git a/src/test/java/org/wickedsource/docxstamper/test/FailOnUnresolvedExpressionTest.java b/src/test/java/org/wickedsource/docxstamper/test/FailOnUnresolvedPlaceholderTest.java similarity index 97% rename from src/test/java/org/wickedsource/docxstamper/test/FailOnUnresolvedExpressionTest.java rename to src/test/java/org/wickedsource/docxstamper/test/FailOnUnresolvedPlaceholderTest.java index 5629efe2..6e9612f4 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/FailOnUnresolvedExpressionTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/FailOnUnresolvedPlaceholderTest.java @@ -18,7 +18,7 @@ * @author Joseph Verron * @author Tom Hombergs */ -class FailOnUnresolvedExpressionTest { +class FailOnUnresolvedPlaceholderTest { @Test void fails() throws IOException { var context = new Name("Homer"); diff --git a/src/test/java/org/wickedsource/docxstamper/test/ExpressionReplacementInHeaderAndFooterTest.java b/src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java similarity index 98% rename from src/test/java/org/wickedsource/docxstamper/test/ExpressionReplacementInHeaderAndFooterTest.java rename to src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java index 36f99a70..7efdab7a 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/ExpressionReplacementInHeaderAndFooterTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java @@ -23,7 +23,7 @@ * @author Joseph Verron * @author Tom Hombergs */ -class ExpressionReplacementInHeaderAndFooterTest { +class PlaceholderReplacementInHeaderAndFooterTest { @Test void expressionReplacementInHeaderAndFooterTest() throws Docx4JException, IOException { diff --git a/src/test/java/org/wickedsource/docxstamper/test/ExpressionReplacementInTextBoxesTest.java b/src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java similarity index 95% rename from src/test/java/org/wickedsource/docxstamper/test/ExpressionReplacementInTextBoxesTest.java rename to src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java index 83cfaab0..27a93c5e 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/ExpressionReplacementInTextBoxesTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java @@ -15,7 +15,7 @@ * @author Joseph Verron * @author Thomas Oster */ -class ExpressionReplacementInTextBoxesTest { +class PlaceholderReplacementInTextBoxesTest { @Test void expressionReplacementInTextBoxesTest() { var context = new Name("Bart Simpson"); diff --git a/src/test/java/org/wickedsource/docxstamper/test/RepeatDocPartBadExpressionTest.java b/src/test/java/org/wickedsource/docxstamper/test/RepeatDocPartBadPlaceholderTest.java similarity index 94% rename from src/test/java/org/wickedsource/docxstamper/test/RepeatDocPartBadExpressionTest.java rename to src/test/java/org/wickedsource/docxstamper/test/RepeatDocPartBadPlaceholderTest.java index 5befcd37..f0c1cbb1 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/RepeatDocPartBadExpressionTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/RepeatDocPartBadPlaceholderTest.java @@ -24,9 +24,9 @@ * @version ${version} * @since 1.6.6 */ -public class RepeatDocPartBadExpressionTest { +public class RepeatDocPartBadPlaceholderTest { private static final Logger logger = - LoggerFactory.getLogger(RepeatDocPartBadExpressionTest.class); + LoggerFactory.getLogger(RepeatDocPartBadPlaceholderTest.class); @Test @Timeout(10) // in the case of pipe lock because of unknown exceptions diff --git a/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java b/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java index 62451772..75eade76 100644 --- a/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java +++ b/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java @@ -5,9 +5,9 @@ import org.docx4j.wml.R; import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; -import org.wickedsource.docxstamper.replace.PlaceholderReplacer; import org.wickedsource.docxstamper.util.CommentWrapper; import org.wickedsource.docxstamper.util.RunUtil; +import pro.verron.docxstamper.core.PlaceholderReplacer; import java.util.ArrayList; import java.util.List; @@ -39,7 +39,7 @@ public class CustomCommentProcessor /** *

Constructor for CustomCommentProcessor.

* - * @param placeholderReplacer a {@link org.wickedsource.docxstamper.replace.PlaceholderReplacer} object + * @param placeholderReplacer a {@link PlaceholderReplacer} object */ public CustomCommentProcessor(PlaceholderReplacer placeholderReplacer) { super(placeholderReplacer); From b95e01669bf9dcb42aca43f6b24d251cb9d7aa58 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 14 Mar 2024 16:27:13 +0100 Subject: [PATCH 015/134] Implement ParagraphPlaceholderReplacer interface A new ParagraphPlaceholderReplacer interface has been introduced, and PlaceholderReplacer has been made an implementing class. Correspondingly, BaseCommentProcessor and ParagraphResolverDocumentWalker classes are updated to reference ParagraphPlaceholderReplacer instead of PlaceholderReplacer. These changes aim to enhance code modularity and readability. --- .../docxstamper/processor/BaseCommentProcessor.java | 3 ++- .../repeat/ParagraphResolverDocumentWalker.java | 6 +++--- .../api/ParagraphPlaceholderReplacer.java | 12 ++++++++++++ .../verron/docxstamper/core/PlaceholderReplacer.java | 5 ++++- 4 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 src/main/java/pro/verron/docxstamper/api/ParagraphPlaceholderReplacer.java diff --git a/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java index 488e0fb9..de71700e 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java @@ -6,6 +6,7 @@ import org.wickedsource.docxstamper.DocxStamper; import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.util.CommentWrapper; +import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.core.PlaceholderReplacer; import java.util.Objects; @@ -23,7 +24,7 @@ public abstract class BaseCommentProcessor implements ICommentProcessor { /** * PlaceholderReplacer used to replace expressions in the comment text. */ - protected final PlaceholderReplacer placeholderReplacer; + protected final ParagraphPlaceholderReplacer placeholderReplacer; private P paragraph; private R currentRun; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java index 8ed18708..30f5eb78 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java @@ -5,7 +5,7 @@ import org.docx4j.wml.Tr; import org.wickedsource.docxstamper.util.ParagraphWrapper; import org.wickedsource.docxstamper.util.walk.BaseDocumentWalker; -import pro.verron.docxstamper.core.PlaceholderReplacer; +import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; /** * Walks through a document and replaces expressions with values from the given @@ -20,7 +20,7 @@ public class ParagraphResolverDocumentWalker extends BaseDocumentWalker { private final Object expressionContext; private final WordprocessingMLPackage document; - private final PlaceholderReplacer placeholderReplacer; + private final ParagraphPlaceholderReplacer placeholderReplacer; /** *

Constructor for ParagraphResolverDocumentWalker.

@@ -34,7 +34,7 @@ public ParagraphResolverDocumentWalker( Tr rowClone, Object expressionContext, WordprocessingMLPackage document, - PlaceholderReplacer replacer + ParagraphPlaceholderReplacer replacer ) { super(rowClone); this.expressionContext = expressionContext; diff --git a/src/main/java/pro/verron/docxstamper/api/ParagraphPlaceholderReplacer.java b/src/main/java/pro/verron/docxstamper/api/ParagraphPlaceholderReplacer.java new file mode 100644 index 00000000..1da70f5c --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/api/ParagraphPlaceholderReplacer.java @@ -0,0 +1,12 @@ +package pro.verron.docxstamper.api; + +import org.docx4j.openpackaging.packages.WordprocessingMLPackage; +import org.wickedsource.docxstamper.util.ParagraphWrapper; + +public interface ParagraphPlaceholderReplacer { + void resolveExpressionsForParagraph( + ParagraphWrapper paragraph, + Object context, + WordprocessingMLPackage document + ); +} diff --git a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java index 67980118..278b8cef 100644 --- a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java +++ b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java @@ -14,6 +14,7 @@ import org.wickedsource.docxstamper.util.ParagraphWrapper; import org.wickedsource.docxstamper.util.RunUtil; import org.wickedsource.docxstamper.util.walk.BaseCoordinatesWalker; +import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.api.Placeholder; /** @@ -24,7 +25,8 @@ * @version ${version} * @since 1.0.0 */ -public class PlaceholderReplacer { +public class PlaceholderReplacer + implements ParagraphPlaceholderReplacer { private static final Logger log = LoggerFactory.getLogger( PlaceholderReplacer.class); private final ExpressionResolver resolver; @@ -100,6 +102,7 @@ protected void onParagraph(P paragraph) { * @param context the context root * @param document the document in which to replace all expressions. */ + @Override public void resolveExpressionsForParagraph( ParagraphWrapper paragraph, Object context, From d55857153d3518924f6d782763f32f691fae16a9 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 14 Mar 2024 16:27:28 +0100 Subject: [PATCH 016/134] Make Logger in ObjectResolver class final The Logger instance in the ObjectResolver class has been marked as final. This change ensures the Logger instance cannot be modified or reassigned, which can improve thread-safety and overall code stability. --- src/main/java/pro/verron/docxstamper/api/ObjectResolver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java b/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java index f3fe0747..c9130f9a 100644 --- a/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java +++ b/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java @@ -47,7 +47,7 @@ default R resolve( boolean canResolve(Object object); class Log { - static Logger log = LoggerFactory.getLogger(ObjectResolver.class); + static final Logger log = LoggerFactory.getLogger(ObjectResolver.class); } /** From 9e1197499a7028386aad2ccef549a4faf63e8eca Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 14 Mar 2024 16:30:46 +0100 Subject: [PATCH 017/134] Refactor CommentWrapper class to new package Moved the CommentWrapper class from "org.wickedsource.docxstamper.util" package to the "pro.verron.docxstamper.core" package, and modified the corresponding import statements in the affected files. This reallocation organizes the code base better by regrouping this core class with others in the more relevant package. --- .../api/commentprocessor/ICommentProcessor.java | 2 +- .../processor/BaseCommentProcessor.java | 4 ++-- .../processor/CommentProcessorRegistry.java | 2 +- .../processor/repeat/ParagraphRepeatProcessor.java | 6 +++++- .../processor/repeat/RepeatDocPartProcessor.java | 2 +- .../processor/repeat/RepeatProcessor.java | 2 +- .../wickedsource/docxstamper/util/CommentUtil.java | 1 + .../verron/docxstamper/core}/CommentWrapper.java | 14 ++++++++------ .../commentProcessors/CustomCommentProcessor.java | 2 +- 9 files changed, 21 insertions(+), 14 deletions(-) rename src/main/java/{org/wickedsource/docxstamper/util => pro/verron/docxstamper/core}/CommentWrapper.java (93%) diff --git a/src/main/java/org/wickedsource/docxstamper/api/commentprocessor/ICommentProcessor.java b/src/main/java/org/wickedsource/docxstamper/api/commentprocessor/ICommentProcessor.java index ddd1a4b1..0b68b1cb 100644 --- a/src/main/java/org/wickedsource/docxstamper/api/commentprocessor/ICommentProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/api/commentprocessor/ICommentProcessor.java @@ -3,7 +3,7 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.P; import org.docx4j.wml.R; -import org.wickedsource.docxstamper.util.CommentWrapper; +import pro.verron.docxstamper.core.CommentWrapper; /** *

In a .docx template used by DocxStamper, you can comment paragraphs of text to manipulate them. The comments in diff --git a/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java index de71700e..0b9384ba 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java @@ -5,8 +5,8 @@ import org.docx4j.wml.R; import org.wickedsource.docxstamper.DocxStamper; import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; -import org.wickedsource.docxstamper.util.CommentWrapper; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; +import pro.verron.docxstamper.core.CommentWrapper; import pro.verron.docxstamper.core.PlaceholderReplacer; import java.util.Objects; @@ -74,7 +74,7 @@ public void setDocument(WordprocessingMLPackage document) { /** *

Getter for the field currentCommentWrapper.

* - * @return a {@link org.wickedsource.docxstamper.util.CommentWrapper} object + * @return a {@link CommentWrapper} object */ public CommentWrapper getCurrentCommentWrapper() { return currentCommentWrapper; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java index 8c346a23..aad00ab9 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java @@ -14,10 +14,10 @@ import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.el.ExpressionResolver; import org.wickedsource.docxstamper.util.CommentUtil; -import org.wickedsource.docxstamper.util.CommentWrapper; import org.wickedsource.docxstamper.util.ParagraphWrapper; import org.wickedsource.docxstamper.util.RunUtil; import org.wickedsource.docxstamper.util.walk.BaseCoordinatesWalker; +import pro.verron.docxstamper.core.CommentWrapper; import pro.verron.docxstamper.core.Expressions; import java.math.BigInteger; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java index ea22ddc4..890464ae 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java @@ -6,7 +6,11 @@ import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; -import org.wickedsource.docxstamper.util.*; +import org.wickedsource.docxstamper.util.CommentUtil; +import org.wickedsource.docxstamper.util.ParagraphUtil; +import org.wickedsource.docxstamper.util.ParagraphWrapper; +import org.wickedsource.docxstamper.util.SectionUtil; +import pro.verron.docxstamper.core.CommentWrapper; import pro.verron.docxstamper.core.PlaceholderReplacer; import java.math.BigInteger; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java index 45557851..308546fb 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java @@ -8,11 +8,11 @@ import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; -import org.wickedsource.docxstamper.util.CommentWrapper; import org.wickedsource.docxstamper.util.DocumentUtil; import org.wickedsource.docxstamper.util.ParagraphUtil; import org.wickedsource.docxstamper.util.SectionUtil; import pro.verron.docxstamper.OpcStamper; +import pro.verron.docxstamper.core.CommentWrapper; import pro.verron.docxstamper.core.PlaceholderReplacer; import java.io.IOException; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java index 48d126bb..92720b2c 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java @@ -7,7 +7,7 @@ import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.processor.CommentProcessingException; import org.wickedsource.docxstamper.util.CommentUtil; -import org.wickedsource.docxstamper.util.CommentWrapper; +import pro.verron.docxstamper.core.CommentWrapper; import pro.verron.docxstamper.core.PlaceholderReplacer; import java.math.BigInteger; diff --git a/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java b/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java index 53210ad2..6e80d414 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java +++ b/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java @@ -15,6 +15,7 @@ import org.wickedsource.docxstamper.util.walk.BaseDocumentWalker; import org.wickedsource.docxstamper.util.walk.DocumentWalker; import pro.verron.docxstamper.api.Placeholder; +import pro.verron.docxstamper.core.CommentWrapper; import pro.verron.docxstamper.core.Expressions; import java.math.BigInteger; diff --git a/src/main/java/org/wickedsource/docxstamper/util/CommentWrapper.java b/src/main/java/pro/verron/docxstamper/core/CommentWrapper.java similarity index 93% rename from src/main/java/org/wickedsource/docxstamper/util/CommentWrapper.java rename to src/main/java/pro/verron/docxstamper/core/CommentWrapper.java index 95577fec..08e9028f 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/CommentWrapper.java +++ b/src/main/java/pro/verron/docxstamper/core/CommentWrapper.java @@ -1,4 +1,4 @@ -package org.wickedsource.docxstamper.util; +package pro.verron.docxstamper.core; import org.docx4j.XmlUtils; import org.docx4j.jaxb.Context; @@ -10,6 +10,8 @@ import org.docx4j.wml.R.CommentReference; import org.jvnet.jaxb2_commons.ppp.Child; import org.wickedsource.docxstamper.api.DocxStamperException; +import org.wickedsource.docxstamper.util.CommentUtil; +import org.wickedsource.docxstamper.util.DocumentUtil; import java.util.*; import java.util.stream.Collectors; @@ -30,23 +32,23 @@ public class CommentWrapper { private CommentRangeEnd commentRangeEnd; private CommentReference commentReference; - void setComment(Comment comment) { + public void setComment(Comment comment) { this.comment = comment; } - void setCommentRangeStart(CommentRangeStart commentRangeStart) { + public void setCommentRangeStart(CommentRangeStart commentRangeStart) { this.commentRangeStart = commentRangeStart; } - void setCommentRangeEnd(CommentRangeEnd commentRangeEnd) { + public void setCommentRangeEnd(CommentRangeEnd commentRangeEnd) { this.commentRangeEnd = commentRangeEnd; } - void setCommentReference(CommentReference commentReference) { + public void setCommentReference(CommentReference commentReference) { this.commentReference = commentReference; } - void setChildren(Set children) { + public void setChildren(Set children) { this.children.addAll(children); } diff --git a/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java b/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java index 75eade76..429a6f97 100644 --- a/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java +++ b/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java @@ -5,8 +5,8 @@ import org.docx4j.wml.R; import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; -import org.wickedsource.docxstamper.util.CommentWrapper; import org.wickedsource.docxstamper.util.RunUtil; +import pro.verron.docxstamper.core.CommentWrapper; import pro.verron.docxstamper.core.PlaceholderReplacer; import java.util.ArrayList; From f1e722a71ea3814130507ecb4c4d2b5e01912bc5 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 14 Mar 2024 16:47:04 +0100 Subject: [PATCH 018/134] Make CommentWrapper publicly accessible and move to core package Moved CommentWrapper to the "pro.verron.docxstamper.core" package, and made it more accessible by changing the class from package-private to public. Other files have been updated to import CommentWrapper from the new location. This move makes the structure of the project more logical and enhances code organisation. --- src/main/java/module-info.java | 3 +- .../docxstamper/DocxStamperConfiguration.java | 2 +- .../commentprocessor/ICommentProcessor.java | 2 +- .../processor/BaseCommentProcessor.java | 5 +-- .../processor/CommentProcessorRegistry.java | 8 ++-- .../repeat/ParagraphRepeatProcessor.java | 4 +- .../repeat/RepeatDocPartProcessor.java | 2 +- .../processor/repeat/RepeatProcessor.java | 4 +- .../typeresolver/LegacyFallbackResolver.java | 2 +- .../docxstamper/api/CommentWrapper.java | 37 +++++++++++++++++++ .../verron/docxstamper/core}/CommentUtil.java | 8 ++-- ...rapper.java => DefaultCommentWrapper.java} | 18 +++++++-- .../preset/{resolver => }/Resolvers.java | 5 ++- .../preset/resolver/DateResolver.java | 2 +- .../preset/resolver/ImageResolver.java | 2 +- .../preset/resolver/LocalDateResolver.java | 2 +- .../resolver/LocalDateTimeResolver.java | 2 +- .../preset/resolver/LocalTimeResolver.java | 2 +- .../preset/resolver/Null2DefaultResolver.java | 3 +- .../resolver/Null2PlaceholderResolver.java | 5 ++- .../preset/resolver/ToStringResolver.java | 2 +- .../docxstamper/test/DefaultTests.java | 2 +- .../CustomCommentProcessor.java | 8 ++-- .../docxstamper/test/utils/Stringifier.java | 2 +- 24 files changed, 92 insertions(+), 40 deletions(-) create mode 100644 src/main/java/pro/verron/docxstamper/api/CommentWrapper.java rename src/main/java/{org/wickedsource/docxstamper/util => pro/verron/docxstamper/core}/CommentUtil.java (98%) rename src/main/java/pro/verron/docxstamper/core/{CommentWrapper.java => DefaultCommentWrapper.java} (95%) rename src/main/java/pro/verron/docxstamper/preset/{resolver => }/Resolvers.java (97%) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index b7c703cc..983668d8 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,6 +1,7 @@ module pro.verron.opcstamper { exports pro.verron.docxstamper.api; - exports pro.verron.docxstamper.preset.resolver; + //exports pro.verron.docxstamper.core; + exports pro.verron.docxstamper.preset; exports org.wickedsource.docxstamper; exports org.wickedsource.docxstamper.el; diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java index dd588c39..76ade30a 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java @@ -18,8 +18,8 @@ import org.wickedsource.docxstamper.processor.table.ITableResolver; import org.wickedsource.docxstamper.replace.typeresolver.TypeResolver; import pro.verron.docxstamper.api.ObjectResolver; +import pro.verron.docxstamper.preset.Resolvers; import pro.verron.docxstamper.preset.resolver.Null2DefaultResolver; -import pro.verron.docxstamper.preset.resolver.Resolvers; import java.util.*; diff --git a/src/main/java/org/wickedsource/docxstamper/api/commentprocessor/ICommentProcessor.java b/src/main/java/org/wickedsource/docxstamper/api/commentprocessor/ICommentProcessor.java index 0b68b1cb..cdda0bc7 100644 --- a/src/main/java/org/wickedsource/docxstamper/api/commentprocessor/ICommentProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/api/commentprocessor/ICommentProcessor.java @@ -3,7 +3,7 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.P; import org.docx4j.wml.R; -import pro.verron.docxstamper.core.CommentWrapper; +import pro.verron.docxstamper.api.CommentWrapper; /** *

In a .docx template used by DocxStamper, you can comment paragraphs of text to manipulate them. The comments in diff --git a/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java index 0b9384ba..96d458c4 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java @@ -5,9 +5,8 @@ import org.docx4j.wml.R; import org.wickedsource.docxstamper.DocxStamper; import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; +import pro.verron.docxstamper.api.CommentWrapper; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; -import pro.verron.docxstamper.core.CommentWrapper; -import pro.verron.docxstamper.core.PlaceholderReplacer; import java.util.Objects; @@ -36,7 +35,7 @@ public abstract class BaseCommentProcessor implements ICommentProcessor { * * @param placeholderReplacer PlaceholderReplacer used to replace placeholders in the comment text. */ - protected BaseCommentProcessor(PlaceholderReplacer placeholderReplacer) { + protected BaseCommentProcessor(ParagraphPlaceholderReplacer placeholderReplacer) { this.placeholderReplacer = placeholderReplacer; } diff --git a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java index aad00ab9..e29a0e0f 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java @@ -13,11 +13,11 @@ import org.wickedsource.docxstamper.api.UnresolvedExpressionException; import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.el.ExpressionResolver; -import org.wickedsource.docxstamper.util.CommentUtil; import org.wickedsource.docxstamper.util.ParagraphWrapper; import org.wickedsource.docxstamper.util.RunUtil; import org.wickedsource.docxstamper.util.walk.BaseCoordinatesWalker; -import pro.verron.docxstamper.core.CommentWrapper; +import pro.verron.docxstamper.api.CommentWrapper; +import pro.verron.docxstamper.core.CommentUtil; import pro.verron.docxstamper.core.Expressions; import java.math.BigInteger; @@ -26,8 +26,8 @@ import java.util.Objects; import java.util.Optional; -import static org.wickedsource.docxstamper.util.CommentUtil.getCommentString; -import static org.wickedsource.docxstamper.util.CommentUtil.getComments; +import static pro.verron.docxstamper.core.CommentUtil.getCommentString; +import static pro.verron.docxstamper.core.CommentUtil.getComments; /** * Allows registration of {@link ICommentProcessor} objects. Each registered diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java index 890464ae..e7dbfcdb 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java @@ -6,11 +6,11 @@ import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; -import org.wickedsource.docxstamper.util.CommentUtil; import org.wickedsource.docxstamper.util.ParagraphUtil; import org.wickedsource.docxstamper.util.ParagraphWrapper; import org.wickedsource.docxstamper.util.SectionUtil; -import pro.verron.docxstamper.core.CommentWrapper; +import pro.verron.docxstamper.api.CommentWrapper; +import pro.verron.docxstamper.core.CommentUtil; import pro.verron.docxstamper.core.PlaceholderReplacer; import java.math.BigInteger; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java index 308546fb..b4e611c3 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java @@ -12,7 +12,7 @@ import org.wickedsource.docxstamper.util.ParagraphUtil; import org.wickedsource.docxstamper.util.SectionUtil; import pro.verron.docxstamper.OpcStamper; -import pro.verron.docxstamper.core.CommentWrapper; +import pro.verron.docxstamper.api.CommentWrapper; import pro.verron.docxstamper.core.PlaceholderReplacer; import java.io.IOException; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java index 92720b2c..10685ea9 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java @@ -6,8 +6,8 @@ import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.processor.CommentProcessingException; -import org.wickedsource.docxstamper.util.CommentUtil; -import pro.verron.docxstamper.core.CommentWrapper; +import pro.verron.docxstamper.api.CommentWrapper; +import pro.verron.docxstamper.core.CommentUtil; import pro.verron.docxstamper.core.PlaceholderReplacer; import java.math.BigInteger; diff --git a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/LegacyFallbackResolver.java b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/LegacyFallbackResolver.java index 47875eba..5a652d13 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/LegacyFallbackResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/LegacyFallbackResolver.java @@ -4,7 +4,7 @@ import org.docx4j.wml.R; import org.wickedsource.docxstamper.util.RunUtil; import pro.verron.docxstamper.api.ObjectResolver; -import pro.verron.docxstamper.preset.resolver.Resolvers; +import pro.verron.docxstamper.preset.Resolvers; /** * The LegacyFallbackResolver served as a fallback when there was no ITypeResolver available for a certain type. diff --git a/src/main/java/pro/verron/docxstamper/api/CommentWrapper.java b/src/main/java/pro/verron/docxstamper/api/CommentWrapper.java new file mode 100644 index 00000000..69e22633 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/api/CommentWrapper.java @@ -0,0 +1,37 @@ +package pro.verron.docxstamper.api; + +import org.docx4j.openpackaging.packages.WordprocessingMLPackage; +import org.docx4j.wml.*; + +import java.util.List; +import java.util.Set; + +public interface CommentWrapper { + ContentAccessor getParent(); + + List getRepeatElements(); + + WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) throws Exception; + + WordprocessingMLPackage tryBuildingSubtemplate(WordprocessingMLPackage document); + + CommentRangeEnd getCommentRangeEnd(); + + void setCommentRangeEnd(CommentRangeEnd commentRangeEnd); + + CommentRangeStart getCommentRangeStart(); + + void setCommentRangeStart(CommentRangeStart commentRangeStart); + + R.CommentReference getCommentReference(); + + void setCommentReference(R.CommentReference commentReference); + + Set getChildren(); + + void setChildren(Set commentWrappers); + + Comments.Comment getComment(); + + void setComment(Comments.Comment comment); +} diff --git a/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java similarity index 98% rename from src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java rename to src/main/java/pro/verron/docxstamper/core/CommentUtil.java index 6e80d414..dee0cc1a 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java +++ b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java @@ -1,4 +1,4 @@ -package org.wickedsource.docxstamper.util; +package pro.verron.docxstamper.core; import org.docx4j.TextUtils; import org.docx4j.openpackaging.exceptions.Docx4JException; @@ -12,11 +12,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wickedsource.docxstamper.api.DocxStamperException; +import org.wickedsource.docxstamper.util.ParagraphWrapper; import org.wickedsource.docxstamper.util.walk.BaseDocumentWalker; import org.wickedsource.docxstamper.util.walk.DocumentWalker; +import pro.verron.docxstamper.api.CommentWrapper; import pro.verron.docxstamper.api.Placeholder; -import pro.verron.docxstamper.core.CommentWrapper; -import pro.verron.docxstamper.core.Expressions; import java.math.BigInteger; import java.util.*; @@ -333,7 +333,7 @@ protected void onCommentRangeStart(CommentRangeStart commentRangeStart) { CommentWrapper commentWrapper = allComments.get( commentRangeStart.getId()); if (commentWrapper == null) { - commentWrapper = new CommentWrapper(); + commentWrapper = new DefaultCommentWrapper(); allComments.put(commentRangeStart.getId(), commentWrapper); if (stack.isEmpty()) { rootComments.put(commentRangeStart.getId(), diff --git a/src/main/java/pro/verron/docxstamper/core/CommentWrapper.java b/src/main/java/pro/verron/docxstamper/core/DefaultCommentWrapper.java similarity index 95% rename from src/main/java/pro/verron/docxstamper/core/CommentWrapper.java rename to src/main/java/pro/verron/docxstamper/core/DefaultCommentWrapper.java index 08e9028f..8bae0a62 100644 --- a/src/main/java/pro/verron/docxstamper/core/CommentWrapper.java +++ b/src/main/java/pro/verron/docxstamper/core/DefaultCommentWrapper.java @@ -10,8 +10,8 @@ import org.docx4j.wml.R.CommentReference; import org.jvnet.jaxb2_commons.ppp.Child; import org.wickedsource.docxstamper.api.DocxStamperException; -import org.wickedsource.docxstamper.util.CommentUtil; import org.wickedsource.docxstamper.util.DocumentUtil; +import pro.verron.docxstamper.api.CommentWrapper; import java.util.*; import java.util.stream.Collectors; @@ -24,7 +24,8 @@ * @version ${version} * @since 1.0.2 */ -public class CommentWrapper { +public class DefaultCommentWrapper + implements CommentWrapper { private final Set children = new HashSet<>(); private Comment comment; @@ -57,6 +58,7 @@ public void setChildren(Set children) { * * @return the comment's author. */ + @Override public ContentAccessor getParent() { return findGreatestCommonParent( getCommentRangeEnd().getParent(), @@ -99,6 +101,7 @@ private ContentAccessor findInsertableParent(ContentAccessor searchFrom) { * * @return the elements in the document that are between the comment range anchors. */ + @Override public List getRepeatElements() { List repeatElements = new ArrayList<>(); boolean startFound = false; @@ -139,6 +142,7 @@ private void extractedSubComments(List commentList, Set * @return a new document containing only the elements between the comment range anchors. * @throws Exception if the sub template could not be created. */ + @Override public WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) throws Exception { List repeatElements = getRepeatElements(); @@ -174,7 +178,10 @@ public WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) * @param document the document from which to copy the elements. * @return a new document containing only the elements between the comment range anchors. */ - public WordprocessingMLPackage tryBuildingSubtemplate(WordprocessingMLPackage document) { + @Override + public WordprocessingMLPackage tryBuildingSubtemplate( + WordprocessingMLPackage document + ) { try { return getSubTemplate(document); } catch (Exception e) { @@ -187,6 +194,7 @@ public WordprocessingMLPackage tryBuildingSubtemplate(WordprocessingMLPackage do * * @return a {@link CommentRangeEnd} object */ + @Override public CommentRangeEnd getCommentRangeEnd() { return commentRangeEnd; } @@ -196,6 +204,7 @@ public CommentRangeEnd getCommentRangeEnd() { * * @return a {@link CommentRangeStart} object */ + @Override public CommentRangeStart getCommentRangeStart() { return commentRangeStart; } @@ -205,6 +214,7 @@ public CommentRangeStart getCommentRangeStart() { * * @return a {@link CommentReference} object */ + @Override public CommentReference getCommentReference() { return commentReference; } @@ -214,6 +224,7 @@ public CommentReference getCommentReference() { * * @return a {@link Set} object */ + @Override public Set getChildren() { return children; } @@ -223,6 +234,7 @@ public Set getChildren() { * * @return a {@link Comment} object */ + @Override public Comment getComment() { return comment; } diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/Resolvers.java b/src/main/java/pro/verron/docxstamper/preset/Resolvers.java similarity index 97% rename from src/main/java/pro/verron/docxstamper/preset/resolver/Resolvers.java rename to src/main/java/pro/verron/docxstamper/preset/Resolvers.java index 6f600077..97e73523 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/Resolvers.java +++ b/src/main/java/pro/verron/docxstamper/preset/Resolvers.java @@ -1,7 +1,8 @@ -package pro.verron.docxstamper.preset.resolver; +package pro.verron.docxstamper.preset; -import org.wickedsource.docxstamper.replace.typeresolver.image.Image; +import pro.verron.docxstamper.api.Image; import pro.verron.docxstamper.api.ObjectResolver; +import pro.verron.docxstamper.preset.resolver.*; import java.time.LocalDate; import java.time.LocalDateTime; diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/DateResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/DateResolver.java index c048aca5..bd7f8f18 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/DateResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/DateResolver.java @@ -16,7 +16,7 @@ * @version ${version} * @since 1.6.7 */ -final class DateResolver +public final class DateResolver extends StringResolver { private final DateTimeFormatter formatter; diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java index 7e571580..c7acdc08 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java @@ -19,7 +19,7 @@ * @version ${version} * @since 1.6.7 */ -class ImageResolver +public class ImageResolver implements ObjectResolver { /** diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateResolver.java index 1387929f..97dfe5bb 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateResolver.java @@ -12,7 +12,7 @@ * @version ${version} * @since 1.6.4 */ -final class LocalDateResolver +public final class LocalDateResolver extends StringResolver { private final DateTimeFormatter formatter; diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateTimeResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateTimeResolver.java index 1fb265f3..550feb63 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateTimeResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateTimeResolver.java @@ -12,7 +12,7 @@ * @version ${version} * @since 1.6.4 */ -final class LocalDateTimeResolver +public final class LocalDateTimeResolver extends StringResolver { private final DateTimeFormatter formatter; diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/LocalTimeResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/LocalTimeResolver.java index f55d1960..4f3202e3 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/LocalTimeResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/LocalTimeResolver.java @@ -12,7 +12,7 @@ * @version ${version} * @since 1.6.4 */ -final class LocalTimeResolver +public final class LocalTimeResolver extends StringResolver { private final DateTimeFormatter formatter; diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/Null2DefaultResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/Null2DefaultResolver.java index 9f16aebd..136a4d5f 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/Null2DefaultResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/Null2DefaultResolver.java @@ -23,7 +23,8 @@ public class Null2DefaultResolver private final String text; - /* package */ Null2DefaultResolver(String text) { + /* package */ + public Null2DefaultResolver(String text) { this.text = text; } diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/Null2PlaceholderResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/Null2PlaceholderResolver.java index 973148c6..b2983db5 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/Null2PlaceholderResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/Null2PlaceholderResolver.java @@ -15,10 +15,11 @@ * @version ${version} * @since 1.6.7 */ -class Null2PlaceholderResolver +public class Null2PlaceholderResolver implements ObjectResolver { - /* package */ Null2PlaceholderResolver() { + /* package */ + public Null2PlaceholderResolver() { //DO NOTHING } diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/ToStringResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/ToStringResolver.java index ea1b1d86..c3050867 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/ToStringResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/ToStringResolver.java @@ -14,7 +14,7 @@ * * @version ${version} * * @since 1.6.7 */ -class ToStringResolver +public class ToStringResolver implements ObjectResolver { @Override public boolean canResolve(Object object) { diff --git a/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java b/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java index e82d4468..ecacc1b1 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java +++ b/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java @@ -10,7 +10,7 @@ import org.wickedsource.docxstamper.DocxStamperConfiguration; import org.wickedsource.docxstamper.el.NoOpEvaluationContextConfigurer; import pro.verron.docxstamper.api.Image; -import pro.verron.docxstamper.preset.resolver.Resolvers; +import pro.verron.docxstamper.preset.Resolvers; import pro.verron.docxstamper.test.Functions; import pro.verron.docxstamper.test.accessors.SimpleGetter; import pro.verron.docxstamper.test.commentProcessors.CustomCommentProcessor; diff --git a/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java b/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java index 429a6f97..c9ad745b 100644 --- a/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java +++ b/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java @@ -6,8 +6,8 @@ import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.util.RunUtil; -import pro.verron.docxstamper.core.CommentWrapper; -import pro.verron.docxstamper.core.PlaceholderReplacer; +import pro.verron.docxstamper.api.CommentWrapper; +import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import java.util.ArrayList; import java.util.List; @@ -39,9 +39,9 @@ public class CustomCommentProcessor /** *

Constructor for CustomCommentProcessor.

* - * @param placeholderReplacer a {@link PlaceholderReplacer} object + * @param placeholderReplacer a {@link ParagraphPlaceholderReplacer} object */ - public CustomCommentProcessor(PlaceholderReplacer placeholderReplacer) { + public CustomCommentProcessor(ParagraphPlaceholderReplacer placeholderReplacer) { super(placeholderReplacer); } diff --git a/src/test/java/pro/verron/docxstamper/test/utils/Stringifier.java b/src/test/java/pro/verron/docxstamper/test/utils/Stringifier.java index 92011a5f..632b489d 100644 --- a/src/test/java/pro/verron/docxstamper/test/utils/Stringifier.java +++ b/src/test/java/pro/verron/docxstamper/test/utils/Stringifier.java @@ -14,7 +14,7 @@ import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage; import org.docx4j.wml.*; import org.wickedsource.docxstamper.api.DocxStamperException; -import org.wickedsource.docxstamper.util.CommentUtil; +import pro.verron.docxstamper.core.CommentUtil; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; From 110e640763eb05ce6116da4ff0421a8f440c364e Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 14 Mar 2024 16:57:56 +0100 Subject: [PATCH 019/134] Refine wording in comments and docs This commit primarily refines wording in code comments and documentation for better clarity and conciseness. These changes include using more precise language and fixing a few grammatical issues. While not affecting functionality, good documentation and comments improve the understandability of the codebase. --- .github/dependabot.yml | 5 ++--- CONTRIBUTING.md | 8 ++++++-- .../docxstamper/el/StandardMethodResolver.java | 2 +- .../verron/docxstamper/test/utils/context/Contexts.java | 4 ++-- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1f63bf5f..5c18bb79 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,5 +1,4 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. +# To get started with Dependabot version updates, you'll need to specify which package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates @@ -8,4 +7,4 @@ updates: - package-ecosystem: "maven" # See documentation for possible values directory: "/" # Location of package manifests schedule: - interval: "weekly" \ No newline at end of file + interval: "weekly" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4e0ef43a..f2cf38cf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,7 +5,8 @@ First off, thanks for taking the time to contribute! ❤️ All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. 🎉 -> And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about: +> And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support +> the project and show your appreciation, which we would also be thrilled about: > - Star the project > - Tweet about it > - Refer this project in your project's readme @@ -120,7 +121,10 @@ This section guides you through submitting an enhancement suggestion for Docx St - Make sure that you are using the latest version. - Read the [documentation](https://verronpro.github.io/docx-stamper/) carefully and find out if the functionality is already covered, maybe by an individual configuration. - Perform a [search](https://github.com/verronpro/docx-stamper/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. -- Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library. +- Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to + convince the project's developers of this feature merits. Keep in mind that we want features that will be useful to + the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing + an add-on/plugin library. #### How Do I Submit a Good Enhancement Suggestion? diff --git a/src/main/java/org/wickedsource/docxstamper/el/StandardMethodResolver.java b/src/main/java/org/wickedsource/docxstamper/el/StandardMethodResolver.java index c62c04b8..dcece8c9 100644 --- a/src/main/java/org/wickedsource/docxstamper/el/StandardMethodResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/el/StandardMethodResolver.java @@ -81,7 +81,7 @@ private boolean methodEquals(Method actualMethod, String expectedName, List expectedType = expectedArguments.get(i) != null ? expectedArguments.get(i).getType() : null; Class actualType = actualMethod.getParameterTypes()[i]; - // null is allowed in place of any type of argument + // null is allowed in place of any argument type if (expectedType != null && !actualType.isAssignableFrom(expectedType)) { return false; } diff --git a/src/test/java/pro/verron/docxstamper/test/utils/context/Contexts.java b/src/test/java/pro/verron/docxstamper/test/utils/context/Contexts.java index f38ab75e..28fc1c4c 100644 --- a/src/test/java/pro/verron/docxstamper/test/utils/context/Contexts.java +++ b/src/test/java/pro/verron/docxstamper/test/utils/context/Contexts.java @@ -345,7 +345,7 @@ public NullishContext( } /** - * Returns the value of the fullish_value attribute of the NullishContext object. + * Returns the value of the fullish_value attribute in the NullishContext object. * * @return The value of the fullish_value attribute. */ @@ -381,7 +381,7 @@ public void setFullish(SubContext fullish) { } /** - * Returns the value of the nullish_value attribute of the NullishContext object. + * Returns the value of the nullish_value attribute in the NullishContext object. * * @return The value of the nullish_value attribute. */ From a4b1d37b31ab05dbe8f73e04fbde313f3223dfd4 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 14 Mar 2024 16:58:10 +0100 Subject: [PATCH 020/134] Remove commented code in module-info.java This commit removes a line of commented code from module-info.java in the pro.verron.docxstamper.api. The removal of unnecessary commented code reduces clutter and improves the overall readability and maintenance of the codebase. --- src/main/java/module-info.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 983668d8..0287378f 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,6 +1,5 @@ module pro.verron.opcstamper { exports pro.verron.docxstamper.api; - //exports pro.verron.docxstamper.core; exports pro.verron.docxstamper.preset; exports org.wickedsource.docxstamper; From d5b4d18a4fceb9ab06d7e764e7334fc8065a8554 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 14 Mar 2024 17:20:05 +0100 Subject: [PATCH 021/134] Remove ParagraphWrapper class references References to the class ParagraphWrapper have been replaced with pro.verron.docxstamper.core.Paragraph. Additional changes include replacing PlaceholderReplacer with ParagraphPlaceholderReplacer where required and handling Header and Footer parts in tests having header and footer. This improves conformity and ensures the correct classes are utilized in the project. --- src/main/java/module-info.java | 3 +- .../docxstamper/CommentProcessorBuilder.java | 4 +- .../processor/CommentProcessorFactory.java | 13 +- .../processor/CommentProcessorRegistry.java | 4 +- .../displayif/DisplayIfProcessor.java | 5 +- .../repeat/ParagraphRepeatProcessor.java | 9 +- .../ParagraphResolverDocumentWalker.java | 4 +- .../repeat/RepeatDocPartProcessor.java | 5 +- .../processor/repeat/RepeatProcessor.java | 5 +- .../ReplaceWithProcessor.java | 5 +- .../processor/table/TableResolver.java | 5 +- .../api/ParagraphPlaceholderReplacer.java | 4 +- .../verron/docxstamper/core/CommentUtil.java | 3 +- .../verron/docxstamper/core/Expressions.java | 3 +- .../verron/docxstamper/core/Paragraph.java} | 9 +- .../docxstamper/core/PlaceholderReplacer.java | 7 +- .../docxstamper/test/ObjectDeleterTest.java | 197 ----------------- .../test/ParagraphWrapperTest.java | 36 ---- ...olderReplacementInHeaderAndFooterTest.java | 92 ++------ .../test/utils/TestDocxStamper.java | 202 +++++++++++------- 20 files changed, 190 insertions(+), 425 deletions(-) rename src/main/java/{org/wickedsource/docxstamper/util/ParagraphWrapper.java => pro/verron/docxstamper/core/Paragraph.java} (97%) delete mode 100644 src/test/java/org/wickedsource/docxstamper/test/ObjectDeleterTest.java delete mode 100644 src/test/java/org/wickedsource/docxstamper/test/ParagraphWrapperTest.java diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 0287378f..81c07b3a 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,5 +1,6 @@ module pro.verron.opcstamper { exports pro.verron.docxstamper.api; + exports pro.verron.docxstamper.core; exports pro.verron.docxstamper.preset; exports org.wickedsource.docxstamper; @@ -9,7 +10,7 @@ exports org.wickedsource.docxstamper.api.commentprocessor; exports org.wickedsource.docxstamper.processor; exports org.wickedsource.docxstamper.api; - exports pro.verron.docxstamper.core; + requires org.apache.commons.io; requires org.docx4j.core; diff --git a/src/main/java/org/wickedsource/docxstamper/CommentProcessorBuilder.java b/src/main/java/org/wickedsource/docxstamper/CommentProcessorBuilder.java index f1a454d0..89359be4 100644 --- a/src/main/java/org/wickedsource/docxstamper/CommentProcessorBuilder.java +++ b/src/main/java/org/wickedsource/docxstamper/CommentProcessorBuilder.java @@ -1,7 +1,7 @@ package org.wickedsource.docxstamper; import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; -import pro.verron.docxstamper.core.PlaceholderReplacer; +import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; /** * Factory interface for creating {@link ICommentProcessor} instances. @@ -17,5 +17,5 @@ public interface CommentProcessorBuilder { * @param placeholderReplacer the placeholder replacer that should be used by the comment processor. * @return a {@link ICommentProcessor} instance. */ - Object create(PlaceholderReplacer placeholderReplacer); + Object create(ParagraphPlaceholderReplacer placeholderReplacer); } diff --git a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorFactory.java b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorFactory.java index 84b684e2..9b89fe0e 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorFactory.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorFactory.java @@ -11,6 +11,7 @@ import org.wickedsource.docxstamper.processor.replaceExpression.ReplaceWithProcessor; import org.wickedsource.docxstamper.processor.table.TableResolver; import pro.verron.docxstamper.OpcStamper; +import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.core.PlaceholderReplacer; /** @@ -38,7 +39,7 @@ public CommentProcessorFactory(DocxStamperConfiguration configuration) { * @param pr a {@link PlaceholderReplacer} object * @return a {@link ICommentProcessor} object */ - public ICommentProcessor repeatParagraph(PlaceholderReplacer pr) { + public ICommentProcessor repeatParagraph(ParagraphPlaceholderReplacer pr) { return ParagraphRepeatProcessor.newInstance(pr); } @@ -48,7 +49,7 @@ public ICommentProcessor repeatParagraph(PlaceholderReplacer pr) { * @param pr a {@link PlaceholderReplacer} object * @return a {@link ICommentProcessor} object */ - public ICommentProcessor repeatDocPart(PlaceholderReplacer pr) { + public ICommentProcessor repeatDocPart(ParagraphPlaceholderReplacer pr) { return RepeatDocPartProcessor.newInstance(pr, getStamper()); } @@ -62,7 +63,7 @@ private OpcStamper getStamper() { * @param pr a {@link PlaceholderReplacer} object * @return a {@link ICommentProcessor} object */ - public ICommentProcessor repeat(PlaceholderReplacer pr) { + public ICommentProcessor repeat(ParagraphPlaceholderReplacer pr) { return RepeatProcessor.newInstance(pr); } @@ -72,7 +73,7 @@ public ICommentProcessor repeat(PlaceholderReplacer pr) { * @param pr a {@link PlaceholderReplacer} object * @return a {@link ICommentProcessor} object */ - public ICommentProcessor tableResolver(PlaceholderReplacer pr) { + public ICommentProcessor tableResolver(ParagraphPlaceholderReplacer pr) { return TableResolver.newInstance(pr); } @@ -82,7 +83,7 @@ public ICommentProcessor tableResolver(PlaceholderReplacer pr) { * @param pr a {@link PlaceholderReplacer} object * @return a {@link ICommentProcessor} object */ - public ICommentProcessor displayIf(PlaceholderReplacer pr) { + public ICommentProcessor displayIf(ParagraphPlaceholderReplacer pr) { return DisplayIfProcessor.newInstance(pr); } @@ -92,7 +93,7 @@ public ICommentProcessor displayIf(PlaceholderReplacer pr) { * @param pr a {@link PlaceholderReplacer} object * @return a {@link ICommentProcessor} object */ - public ICommentProcessor replaceWith(PlaceholderReplacer pr) { + public ICommentProcessor replaceWith(ParagraphPlaceholderReplacer pr) { return ReplaceWithProcessor.newInstance(pr); } } diff --git a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java index e29a0e0f..58d10097 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java @@ -13,12 +13,12 @@ import org.wickedsource.docxstamper.api.UnresolvedExpressionException; import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.el.ExpressionResolver; -import org.wickedsource.docxstamper.util.ParagraphWrapper; import org.wickedsource.docxstamper.util.RunUtil; import org.wickedsource.docxstamper.util.walk.BaseCoordinatesWalker; import pro.verron.docxstamper.api.CommentWrapper; import pro.verron.docxstamper.core.CommentUtil; import pro.verron.docxstamper.core.Expressions; +import pro.verron.docxstamper.core.Paragraph; import java.math.BigInteger; import java.util.ArrayList; @@ -157,7 +157,7 @@ private void runProcessorsOnInlineContent( T expressionContext, P paragraph ) { - var paragraphWrapper = new ParagraphWrapper(paragraph); + var paragraphWrapper = new Paragraph(paragraph); String text = paragraphWrapper.getText(); var expressions = Expressions.findProcessors(text); diff --git a/src/main/java/org/wickedsource/docxstamper/processor/displayif/DisplayIfProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/displayif/DisplayIfProcessor.java index 390c37db..63dc4e58 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/displayif/DisplayIfProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/displayif/DisplayIfProcessor.java @@ -9,6 +9,7 @@ import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.processor.CommentProcessingException; import org.wickedsource.docxstamper.util.ObjectDeleter; +import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.core.PlaceholderReplacer; import java.util.ArrayList; @@ -28,7 +29,7 @@ public class DisplayIfProcessor extends BaseCommentProcessor implements IDisplay private List tablesToBeRemoved = new ArrayList<>(); private List tableRowsToBeRemoved = new ArrayList<>(); - private DisplayIfProcessor(PlaceholderReplacer placeholderReplacer) { + private DisplayIfProcessor(ParagraphPlaceholderReplacer placeholderReplacer) { super(placeholderReplacer); } @@ -38,7 +39,7 @@ private DisplayIfProcessor(PlaceholderReplacer placeholderReplacer) { * @param pr the {@link PlaceholderReplacer} used for replacing expressions. * @return a new DisplayIfProcessor instance. */ - public static ICommentProcessor newInstance(PlaceholderReplacer pr) { + public static ICommentProcessor newInstance(ParagraphPlaceholderReplacer pr) { return new DisplayIfProcessor(pr); } diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java index e7dbfcdb..1a64812d 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java @@ -7,10 +7,11 @@ import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.util.ParagraphUtil; -import org.wickedsource.docxstamper.util.ParagraphWrapper; import org.wickedsource.docxstamper.util.SectionUtil; import pro.verron.docxstamper.api.CommentWrapper; +import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.core.CommentUtil; +import pro.verron.docxstamper.core.Paragraph; import pro.verron.docxstamper.core.PlaceholderReplacer; import java.math.BigInteger; @@ -41,7 +42,7 @@ public class ParagraphRepeatProcessor * @param nullSupplier supplies a list of paragraphs if the list of objects to repeat is null */ private ParagraphRepeatProcessor( - PlaceholderReplacer placeholderReplacer, + ParagraphPlaceholderReplacer placeholderReplacer, Supplier> nullSupplier ) { super(placeholderReplacer); @@ -71,7 +72,7 @@ public static ICommentProcessor newInstance( * @param placeholderReplacer replaces expressions with values * @return a new instance of ParagraphRepeatProcessor */ - public static ICommentProcessor newInstance(PlaceholderReplacer placeholderReplacer) { + public static ICommentProcessor newInstance(ParagraphPlaceholderReplacer placeholderReplacer) { return new ParagraphRepeatProcessor(placeholderReplacer, Collections::emptyList); } @@ -200,7 +201,7 @@ private Deque

generateParagraphsToAdd( paragraphs.commentWrapper.getComment() .getId()); placeholderReplacer.resolveExpressionsForParagraph( - new ParagraphWrapper(pClone), + new Paragraph(pClone), expressionContext, document ); diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java index 30f5eb78..509bf73b 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java @@ -3,9 +3,9 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.P; import org.docx4j.wml.Tr; -import org.wickedsource.docxstamper.util.ParagraphWrapper; import org.wickedsource.docxstamper.util.walk.BaseDocumentWalker; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; +import pro.verron.docxstamper.core.Paragraph; /** * Walks through a document and replaces expressions with values from the given @@ -48,7 +48,7 @@ public ParagraphResolverDocumentWalker( @Override protected void onParagraph(P paragraph) { placeholderReplacer.resolveExpressionsForParagraph( - new ParagraphWrapper(paragraph), + new Paragraph(paragraph), expressionContext, document ); } diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java index b4e611c3..50a1e41d 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java @@ -13,6 +13,7 @@ import org.wickedsource.docxstamper.util.SectionUtil; import pro.verron.docxstamper.OpcStamper; import pro.verron.docxstamper.api.CommentWrapper; +import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.core.PlaceholderReplacer; import java.io.IOException; @@ -54,7 +55,7 @@ public class RepeatDocPartProcessor private final Supplier> nullSupplier; private RepeatDocPartProcessor( - PlaceholderReplacer placeholderReplacer, + ParagraphPlaceholderReplacer placeholderReplacer, OpcStamper stamper, Supplier> nullSupplier ) { @@ -90,7 +91,7 @@ public static ICommentProcessor newInstance( * @return a new instance of this processor */ public static ICommentProcessor newInstance( - PlaceholderReplacer pr, + ParagraphPlaceholderReplacer pr, OpcStamper stamper ) { return new RepeatDocPartProcessor(pr, stamper, Collections::emptyList); diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java index 10685ea9..2ee02fbd 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java @@ -7,6 +7,7 @@ import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.processor.CommentProcessingException; import pro.verron.docxstamper.api.CommentWrapper; +import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.core.CommentUtil; import pro.verron.docxstamper.core.PlaceholderReplacer; @@ -31,7 +32,7 @@ public class RepeatProcessor extends BaseCommentProcessor implements IRepeatProc private Map tableRowsCommentsToRemove = new HashMap<>(); private RepeatProcessor( - PlaceholderReplacer placeholderReplacer, + ParagraphPlaceholderReplacer placeholderReplacer, BiFunction> nullSupplier1 ) { super(placeholderReplacer); @@ -69,7 +70,7 @@ public static List stampEmptyContext(PlaceholderReplacer pr, WordprocessingM * @param pr The PlaceholderReplacer to use. * @return A new RepeatProcessor. */ - public static ICommentProcessor newInstance(PlaceholderReplacer pr) { + public static ICommentProcessor newInstance(ParagraphPlaceholderReplacer pr) { return new RepeatProcessor(pr, (document, row) -> emptyList()); } diff --git a/src/main/java/org/wickedsource/docxstamper/processor/replaceExpression/ReplaceWithProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/replaceExpression/ReplaceWithProcessor.java index 05873f95..e80126bd 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/replaceExpression/ReplaceWithProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/replaceExpression/ReplaceWithProcessor.java @@ -6,6 +6,7 @@ import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.util.RunUtil; +import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.core.PlaceholderReplacer; import java.util.List; @@ -29,7 +30,7 @@ public class ReplaceWithProcessor private final Function> nullSupplier; private ReplaceWithProcessor( - PlaceholderReplacer placeholderReplacer, + ParagraphPlaceholderReplacer placeholderReplacer, Function> nullSupplier ) { super(placeholderReplacer); @@ -58,7 +59,7 @@ public static ICommentProcessor newInstance( * @param pr the placeholder replacer to use * @return the processor */ - public static ICommentProcessor newInstance(PlaceholderReplacer pr) { + public static ICommentProcessor newInstance(ParagraphPlaceholderReplacer pr) { return new ReplaceWithProcessor(pr, R::getContent); } diff --git a/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java b/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java index 3956c5c2..db7057ad 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java @@ -8,6 +8,7 @@ import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.processor.CommentProcessingException; import org.wickedsource.docxstamper.util.ParagraphUtil; +import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.core.PlaceholderReplacer; import java.util.Collections; @@ -30,7 +31,7 @@ public class TableResolver private final Function> nullSupplier; private TableResolver( - PlaceholderReplacer placeholderReplacer, + ParagraphPlaceholderReplacer placeholderReplacer, Function> nullSupplier ) { super(placeholderReplacer); @@ -59,7 +60,7 @@ public static ICommentProcessor newInstance( * @param pr a {@link PlaceholderReplacer} instance * @return a new {@link TableResolver} instance */ - public static ICommentProcessor newInstance(PlaceholderReplacer pr) { + public static ICommentProcessor newInstance(ParagraphPlaceholderReplacer pr) { return new TableResolver(pr, table -> Collections.emptyList()); } diff --git a/src/main/java/pro/verron/docxstamper/api/ParagraphPlaceholderReplacer.java b/src/main/java/pro/verron/docxstamper/api/ParagraphPlaceholderReplacer.java index 1da70f5c..4e9f7fee 100644 --- a/src/main/java/pro/verron/docxstamper/api/ParagraphPlaceholderReplacer.java +++ b/src/main/java/pro/verron/docxstamper/api/ParagraphPlaceholderReplacer.java @@ -1,11 +1,11 @@ package pro.verron.docxstamper.api; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; -import org.wickedsource.docxstamper.util.ParagraphWrapper; +import pro.verron.docxstamper.core.Paragraph; public interface ParagraphPlaceholderReplacer { void resolveExpressionsForParagraph( - ParagraphWrapper paragraph, + Paragraph paragraph, Object context, WordprocessingMLPackage document ); diff --git a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java index dee0cc1a..7a2a4fa5 100644 --- a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java +++ b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java @@ -12,7 +12,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wickedsource.docxstamper.api.DocxStamperException; -import org.wickedsource.docxstamper.util.ParagraphWrapper; import org.wickedsource.docxstamper.util.walk.BaseDocumentWalker; import org.wickedsource.docxstamper.util.walk.DocumentWalker; import pro.verron.docxstamper.api.CommentWrapper; @@ -195,7 +194,7 @@ public static Placeholder getCommentString(Comment comment) { StringBuilder builder = new StringBuilder(); for (Object commentChildObject : comment.getContent()) { if (commentChildObject instanceof P p) { - builder.append(new ParagraphWrapper(p).getText()); + builder.append(new Paragraph(p).getText()); } } String string = builder.toString(); diff --git a/src/main/java/pro/verron/docxstamper/core/Expressions.java b/src/main/java/pro/verron/docxstamper/core/Expressions.java index 1d5b4931..3ecd58f7 100644 --- a/src/main/java/pro/verron/docxstamper/core/Expressions.java +++ b/src/main/java/pro/verron/docxstamper/core/Expressions.java @@ -1,7 +1,6 @@ package pro.verron.docxstamper.core; import org.wickedsource.docxstamper.api.DocxStamperException; -import org.wickedsource.docxstamper.util.ParagraphWrapper; import pro.verron.docxstamper.api.Placeholder; import pro.verron.docxstamper.core.expression.ExpressionFinder; import pro.verron.docxstamper.core.expression.Matcher; @@ -76,7 +75,7 @@ public static List findVariables(String text) { return VAR_FINDER.find(text); } - public static List findVariables(ParagraphWrapper paragraph) { + public static List findVariables(Paragraph paragraph) { return findVariables(paragraph.getText()); } diff --git a/src/main/java/org/wickedsource/docxstamper/util/ParagraphWrapper.java b/src/main/java/pro/verron/docxstamper/core/Paragraph.java similarity index 97% rename from src/main/java/org/wickedsource/docxstamper/util/ParagraphWrapper.java rename to src/main/java/pro/verron/docxstamper/core/Paragraph.java index 16f25f3e..ef9ba005 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/ParagraphWrapper.java +++ b/src/main/java/pro/verron/docxstamper/core/Paragraph.java @@ -1,7 +1,9 @@ -package org.wickedsource.docxstamper.util; +package pro.verron.docxstamper.core; import org.docx4j.wml.P; import org.docx4j.wml.R; +import org.wickedsource.docxstamper.util.IndexedRun; +import org.wickedsource.docxstamper.util.RunUtil; import pro.verron.docxstamper.api.Placeholder; import java.util.ArrayList; @@ -24,8 +26,7 @@ * @version ${version} * @since 1.0.8 */ -// TODO: Rename into Paragraph -public class ParagraphWrapper { +public class Paragraph { private final List runs = new ArrayList<>(); private final P paragraph; private int currentPosition = 0; @@ -35,7 +36,7 @@ public class ParagraphWrapper { * * @param paragraph the paragraph to wrap. */ - public ParagraphWrapper(P paragraph) { + public Paragraph(P paragraph) { this.paragraph = paragraph; recalculateRuns(); } diff --git a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java index 278b8cef..e3b68bca 100644 --- a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java +++ b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java @@ -11,7 +11,6 @@ import org.springframework.expression.spel.SpelParseException; import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.el.ExpressionResolver; -import org.wickedsource.docxstamper.util.ParagraphWrapper; import org.wickedsource.docxstamper.util.RunUtil; import org.wickedsource.docxstamper.util.walk.BaseCoordinatesWalker; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; @@ -88,7 +87,7 @@ public void resolveExpressions( @Override protected void onParagraph(P paragraph) { resolveExpressionsForParagraph( - new ParagraphWrapper(paragraph), + new Paragraph(paragraph), expressionContext, document); } @@ -104,7 +103,7 @@ protected void onParagraph(P paragraph) { */ @Override public void resolveExpressionsForParagraph( - ParagraphWrapper paragraph, + Paragraph paragraph, Object context, WordprocessingMLPackage document ) { @@ -154,7 +153,7 @@ private Placeholder lineBreakPlaceholder() { return lineBreakPlaceholder; } - private void replaceLineBreaks(ParagraphWrapper paragraph) { + private void replaceLineBreaks(Paragraph paragraph) { Br lineBreak = Context.getWmlObjectFactory() .createBr(); R run = RunUtil.create(lineBreak); diff --git a/src/test/java/org/wickedsource/docxstamper/test/ObjectDeleterTest.java b/src/test/java/org/wickedsource/docxstamper/test/ObjectDeleterTest.java deleted file mode 100644 index b53c55da..00000000 --- a/src/test/java/org/wickedsource/docxstamper/test/ObjectDeleterTest.java +++ /dev/null @@ -1,197 +0,0 @@ -package org.wickedsource.docxstamper.test; - -import org.docx4j.openpackaging.exceptions.Docx4JException; -import org.docx4j.openpackaging.packages.WordprocessingMLPackage; -import org.docx4j.wml.P; -import org.docx4j.wml.Tbl; -import org.docx4j.wml.Tc; -import org.docx4j.wml.Tr; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.wickedsource.docxstamper.util.DocumentUtil; -import org.wickedsource.docxstamper.util.ObjectDeleter; -import org.wickedsource.docxstamper.util.ParagraphWrapper; -import pro.verron.docxstamper.test.utils.IOStreams; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.wickedsource.docxstamper.test.DefaultTests.getResource; -import static org.wickedsource.docxstamper.util.DocumentUtil.getParagraphsFromObject; -import static org.wickedsource.docxstamper.util.DocumentUtil.getTableFromObject; - -/** - * @author Joseph Verron - * @author Tom Hombergs - */ -@DisplayName("Utilities - Deletion tools") -class ObjectDeleterTest { - - @Test - void deletesCorrectGlobalParagraphs() throws Docx4JException, IOException { - var template = getResource(Path.of("util","ObjectDeleterTest" + - "-globalParagraphs.docx")); - var in = WordprocessingMLPackage.load(template); - var coordinates = getParagraphsFromObject(in); - - ObjectDeleter.deleteParagraph(coordinates.get(0)); - ObjectDeleter.deleteParagraph(coordinates.get(2)); - ObjectDeleter.deleteParagraph(coordinates.get(3)); - - var document = saveAndLoadDocument(in); - assertEquals(2, document.getMainDocumentPart() - .getContent() - .size()); - assertEquals("This is the second paragraph.", - new ParagraphWrapper((P) document.getMainDocumentPart() - .getContent() - .get(0)).getText()); - assertEquals("This is the fifth paragraph.", - new ParagraphWrapper((P) document.getMainDocumentPart() - .getContent() - .get(1)).getText()); - } - - /** - * Saves the given document into a temporal ByteArrayOutputStream and loads it from there again. This is useful to - * check if changes in the DOCX4J object structure are really transported - * into the XML of the .docx file. - * - * @param document the document to save and load again. - * @return the document after it has been saved and loaded again. - * @throws org.docx4j.openpackaging.exceptions.Docx4JException if any. - * @throws java.io.IOException if any. - * @since 1.6.6 - */ - public WordprocessingMLPackage saveAndLoadDocument(WordprocessingMLPackage document) throws Docx4JException, IOException { - var out = IOStreams.getOutputStream(); - document.save(out); - var in = IOStreams.getInputStream(out); - return WordprocessingMLPackage.load(in); - } - - @Test - void deletesCorrectParagraphsInTableCells() throws Docx4JException, IOException { - var template = getResource( - Path.of("util","ObjectDeleterTest-paragraphsInTableCells" + - ".docx")); - var document = WordprocessingMLPackage.load(template); - final List

coordinates = getParagraphsFromObject( - getTableFromObject(document)); - - ObjectDeleter.deleteParagraph(coordinates.get(1)); - ObjectDeleter.deleteParagraph(coordinates.get(2)); - ObjectDeleter.deleteParagraph(coordinates.get(4)); - ObjectDeleter.deleteParagraph(coordinates.get(5)); - ObjectDeleter.deleteParagraph(coordinates.get(8)); - ObjectDeleter.deleteParagraph(coordinates.get(11)); - - WordprocessingMLPackage savedDocument = saveAndLoadDocument(document); - List cellCoordinates = DocumentUtil.getTableCellsFromObject( - savedDocument); - - assertEquals("0 This paragraph stays.", - new ParagraphWrapper((P) cellCoordinates.get(0) - .getContent() - .get(0)).getText()); - assertEquals("", new ParagraphWrapper((P) cellCoordinates.get(1) - .getContent() - .get(0)).getText()); - assertEquals("3 This is the second paragraph.", - new ParagraphWrapper((P) cellCoordinates.get(2) - .getContent() - .get(0)).getText()); - assertEquals("6 This is the fifth paragraph.", - new ParagraphWrapper((P) cellCoordinates.get(2) - .getContent() - .get(1)).getText()); - assertEquals("7 This is the first paragraph.", - new ParagraphWrapper((P) cellCoordinates.get(3) - .getContent() - .get(0)).getText()); - assertEquals("9 This is the third paragraph.", - new ParagraphWrapper((P) cellCoordinates.get(3) - .getContent() - .get(1)).getText()); - assertEquals("10 This is the fourth paragraph.", - new ParagraphWrapper((P) cellCoordinates.get(3) - .getContent() - .get(2)).getText()); - } - - @Test - void deletesCorrectGlobalTables() throws Docx4JException, IOException { - var template = getResource(Path.of("util","ObjectDeleterTest-tables" + - ".docx")); - var document = WordprocessingMLPackage.load(template); - var coordinates = DocumentUtil.getTableFromObject(document); - - ObjectDeleter.deleteTable(coordinates.get(1)); - ObjectDeleter.deleteTable(coordinates.get(3)); - - var savedDocument = saveAndLoadDocument(document); - - List newTableCoordinates = DocumentUtil.getTableFromObject( - savedDocument); - assertEquals(2, newTableCoordinates.size()); - - List cellCoordinates = DocumentUtil.getTableCellsFromObject( - savedDocument); - assertEquals("This", new ParagraphWrapper((P) cellCoordinates.get(0) - .getContent() - .get(0)).getText()); - assertEquals("Table", new ParagraphWrapper((P) cellCoordinates.get(1) - .getContent() - .get(0)).getText()); - assertEquals("Stays", new ParagraphWrapper((P) cellCoordinates.get(2) - .getContent() - .get(0)).getText()); - assertEquals("!", new ParagraphWrapper((P) cellCoordinates.get(3) - .getContent() - .get(0)).getText()); - assertEquals("This table stays", new ParagraphWrapper( - (P) cellCoordinates.get(4) - .getContent() - .get(0)).getText()); - } - - @Test - void deletesCorrectTableRows() throws Docx4JException, IOException { - var template = getResource(Path.of("util","ObjectDeleterTest" + - "-tableRows.docx")); - var document = WordprocessingMLPackage.load(template); - var rowCoordinates = DocumentUtil.getTableRowsFromObject(document); - - ObjectDeleter.deleteTableRow(rowCoordinates.get(2)); - ObjectDeleter.deleteTableRow(rowCoordinates.get(4)); - - WordprocessingMLPackage savedDocument = saveAndLoadDocument(document); - - List newRowCoordinates = DocumentUtil.getTableRowsFromObject( - savedDocument); - assertEquals(3, newRowCoordinates.size()); - - List cellCoordinates = DocumentUtil.getTableCellsFromObject( - savedDocument); - assertEquals("This row", new ParagraphWrapper((P) cellCoordinates.get(0) - .getContent() - .get(0)).getText()); - assertEquals("Stays!", new ParagraphWrapper((P) cellCoordinates.get(1) - .getContent() - .get(0)).getText()); - assertEquals("This row", new ParagraphWrapper((P) cellCoordinates.get(2) - .getContent() - .get(0)).getText()); - assertEquals("Stays!", new ParagraphWrapper((P) cellCoordinates.get(3) - .getContent() - .get(0)).getText()); - assertEquals("This row", new ParagraphWrapper((P) cellCoordinates.get(4) - .getContent() - .get(0)).getText()); - assertEquals("Stays!", new ParagraphWrapper((P) cellCoordinates.get(5) - .getContent() - .get(0)).getText()); - } -} diff --git a/src/test/java/org/wickedsource/docxstamper/test/ParagraphWrapperTest.java b/src/test/java/org/wickedsource/docxstamper/test/ParagraphWrapperTest.java deleted file mode 100644 index 011469ed..00000000 --- a/src/test/java/org/wickedsource/docxstamper/test/ParagraphWrapperTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.wickedsource.docxstamper.test; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.wickedsource.docxstamper.util.ParagraphWrapper; -import org.wickedsource.docxstamper.util.RunUtil; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.wickedsource.docxstamper.util.ParagraphUtil.create; - -/** - * @author Tom Hombergs - * @author Joseph Verron - */ -@DisplayName("Utilities - Paragraph Wrapper") -class ParagraphWrapperTest { - - @Test - void getTextReturnsAggregatedText() { - ParagraphWrapper aggregator = loremIpsum(); - assertEquals("lorem ipsum", aggregator.getText()); - } - - private ParagraphWrapper loremIpsum() { - return new ParagraphWrapper(create("lorem", " ", "ipsum")); - } - - @Test - void getRunsReturnsAddedRuns() { - ParagraphWrapper aggregator = loremIpsum(); - assertEquals(3, aggregator.getRuns().size()); - assertEquals("lorem", RunUtil.getText(aggregator.getRuns().get(0))); - assertEquals(" ", RunUtil.getText(aggregator.getRuns().get(1))); - assertEquals("ipsum", RunUtil.getText(aggregator.getRuns().get(2))); - } -} diff --git a/src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java b/src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java index 7efdab7a..a15c17ec 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java @@ -1,19 +1,9 @@ package org.wickedsource.docxstamper.test; -import org.docx4j.openpackaging.exceptions.Docx4JException; -import org.docx4j.openpackaging.packages.WordprocessingMLPackage; -import org.docx4j.openpackaging.parts.WordprocessingML.FooterPart; -import org.docx4j.openpackaging.parts.WordprocessingML.HeaderPart; -import org.docx4j.openpackaging.parts.relationships.Namespaces; -import org.docx4j.openpackaging.parts.relationships.RelationshipsPart; -import org.docx4j.relationships.Relationship; -import org.docx4j.wml.P; import org.junit.jupiter.api.Test; import org.wickedsource.docxstamper.DocxStamperConfiguration; -import org.wickedsource.docxstamper.util.ParagraphWrapper; import pro.verron.docxstamper.test.utils.TestDocxStamper; -import java.io.IOException; import java.nio.file.Path; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -24,64 +14,26 @@ * @author Tom Hombergs */ class PlaceholderReplacementInHeaderAndFooterTest { - @Test - void expressionReplacementInHeaderAndFooterTest() throws Docx4JException, - IOException { - var context = new Name("Homer Simpson"); - var template = getResource( - Path.of("ExpressionReplacementInHeaderAndFooterTest.docx")); - var stamper = new TestDocxStamper(new DocxStamperConfiguration().setFailOnUnresolvedExpression(false)); - var document = stamper.stampAndLoad(template, context); - resolvedExpressionsAreReplacedInHeader(document); - resolvedExpressionsAreReplacedInFooter(document); - unresolvedExpressionsAreNotReplacedInHeader(document); - unresolvedExpressionsAreNotReplacedInFooter(document); - } - - private void resolvedExpressionsAreReplacedInHeader(WordprocessingMLPackage document) { - HeaderPart headerPart = getHeaderPart(document); - - P nameParagraph = (P) headerPart.getContent().get(1); - assertEquals("In this paragraph, the variable name should be resolved to the value Homer Simpson.", - new ParagraphWrapper(nameParagraph).getText()); - } - - private void resolvedExpressionsAreReplacedInFooter(WordprocessingMLPackage document) { - FooterPart footerPart = getFooterPart(document); - - P nameParagraph = (P) footerPart.getContent().get(1); - assertEquals("In this paragraph, the variable name should be resolved to the value Homer Simpson.", - new ParagraphWrapper(nameParagraph).getText()); - } - - private void unresolvedExpressionsAreNotReplacedInHeader(WordprocessingMLPackage document) { - HeaderPart headerPart = getHeaderPart(document); - - P fooParagraph = (P) headerPart.getContent().get(2); - assertEquals("In this paragraph, the variable foo should not be resolved: ${foo}.", - new ParagraphWrapper(fooParagraph).getText()); - } - - private void unresolvedExpressionsAreNotReplacedInFooter(WordprocessingMLPackage document) { - FooterPart footerPart = getFooterPart(document); - - P fooParagraph = (P) footerPart.getContent().get(2); - assertEquals("In this paragraph, the variable foo should not be resolved: ${foo}.", - new ParagraphWrapper(fooParagraph).getText()); - } - - private HeaderPart getHeaderPart(WordprocessingMLPackage document) { - RelationshipsPart relPart = document.getMainDocumentPart().getRelationshipsPart(); - Relationship rel = relPart.getRelationshipByType(Namespaces.HEADER); - return (HeaderPart) relPart.getPart(rel); - } - - private FooterPart getFooterPart(WordprocessingMLPackage document) { - RelationshipsPart relPart = document.getMainDocumentPart().getRelationshipsPart(); - Relationship rel = relPart.getRelationshipByType(Namespaces.FOOTER); - return (FooterPart) relPart.getPart(rel); - } - - public record Name(String name) { - } + @Test + void expressionReplacementInHeaderAndFooterTest() { + var context = new Name("Homer Simpson"); + var template = getResource( + Path.of("ExpressionReplacementInHeaderAndFooterTest.docx")); + var stamper = new TestDocxStamper(new DocxStamperConfiguration().setFailOnUnresolvedExpression( + false)); + var actual = stamper.stampAndLoadAndExtract(template, context); + assertEquals(""" + ❬❬This ❘lang=de-DE❭❬header ❘lang=de-DE❭❬paragraph is untouched.❘lang=de-DE❭❘lang=de-DE❭ + ❬❬In this paragraph, the variable ❘lang=de-DE❭❬name❘b=true,lang=de-DE❭ should be resolved to the value ❬Homer Simpson❘lang=de-DE❭.❘lang=de-DE❭ + ❬❬In this paragraph, the variable ❘lang=de-DE❭❬foo❘b=true,lang=de-DE❭❬ should not be resolved: ${foo}.❘lang=de-DE❭❘lang=de-DE,spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭ + ❬Expression Replacement in header and footer❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭ + ❬❬This ❘lang=de-DE❭❬footer ❘lang=de-DE❭❬paragraph is untouched.❘lang=de-DE❭❘lang=de-DE❭ + ❬❬In this paragraph, the variable ❘lang=de-DE❭❬name❘b=true,lang=de-DE❭ should be resolved to the value ❬Homer Simpson❘lang=de-DE❭.❘lang=de-DE❭ + ❬❬In this paragraph, the variable ❘lang=de-DE❭❬foo❘b=true,lang=de-DE❭❬ should not be resolved: ${foo}.❘lang=de-DE❭❘lang=de-DE,spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭""", + actual); + } + + public record Name(String name) { + } } diff --git a/src/test/java/pro/verron/docxstamper/test/utils/TestDocxStamper.java b/src/test/java/pro/verron/docxstamper/test/utils/TestDocxStamper.java index 1d6fdde9..00f66315 100644 --- a/src/test/java/pro/verron/docxstamper/test/utils/TestDocxStamper.java +++ b/src/test/java/pro/verron/docxstamper/test/utils/TestDocxStamper.java @@ -3,6 +3,11 @@ import org.docx4j.TraversalUtil; import org.docx4j.openpackaging.exceptions.Docx4JException; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; +import org.docx4j.openpackaging.parts.WordprocessingML.FooterPart; +import org.docx4j.openpackaging.parts.WordprocessingML.HeaderPart; +import org.docx4j.openpackaging.parts.relationships.Namespaces; +import org.docx4j.openpackaging.parts.relationships.RelationshipsPart; +import org.docx4j.relationships.Relationship; import org.docx4j.wml.P; import org.wickedsource.docxstamper.DocxStamper; import org.wickedsource.docxstamper.DocxStamperConfiguration; @@ -11,6 +16,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.List; +import java.util.Optional; import java.util.stream.Stream; import static java.util.stream.Collectors.joining; @@ -18,96 +24,130 @@ /** * Common methods to interact with docx documents. * - * @since 1.6.5 * @author Joseph Verron * @version ${version} + * @since 1.6.5 */ public final class TestDocxStamper { - private final DocxStamper stamper; - private WordprocessingMLPackage document; + private final DocxStamper stamper; + private WordprocessingMLPackage document; + + /** + *

Constructor for TestDocxStamper.

+ * + * @param config a {@link org.wickedsource.docxstamper.DocxStamperConfiguration} object + * @since 1.6.6 + */ + public TestDocxStamper(DocxStamperConfiguration config) { + stamper = new DocxStamper<>(config); + } + + /** + * Stamps the given template resolving the expressions within the template against the specified context. + * Returns the resulting document after it has been saved and loaded + * again to ensure that changes in the DOCX4J + * object structure were really transported into the XML of the .docx file. + * + * @param template a {@link InputStream} object + * @param context a T object + * @return a {@link WordprocessingMLPackage} object + * @throws IOException if any. + * @throws Docx4JException if any. + * @since 1.6.6 + */ + public WordprocessingMLPackage stampAndLoad( + InputStream template, + T context + ) throws IOException, Docx4JException { + OutputStream out = IOStreams.getOutputStream(); + stamper.stamp(template, context, out); + InputStream in = IOStreams.getInputStream(out); + return WordprocessingMLPackage.load(in); + } + + /** + *

stampAndLoadAndExtract.

+ * + * @param template a {@link InputStream} object + * @param context a T object + * @return a {@link java.util.List} object + * @since 1.6.6 + */ + public String stampAndLoadAndExtract(InputStream template, T context) { + Stringifier stringifier = new Stringifier(() -> document); + return streamElements(template, context, P.class) + .map(stringifier::stringify) + .collect(joining("\n")); + } - /** - *

Constructor for TestDocxStamper.

- * - * @param config a {@link org.wickedsource.docxstamper.DocxStamperConfiguration} object - * @since 1.6.6 - */ - public TestDocxStamper(DocxStamperConfiguration config) { - stamper = new DocxStamper<>(config); - } + private Stream streamElements( + InputStream template, + T context, + Class clazz + ) { + Stream elements; + try { + var out = IOStreams.getOutputStream(); + stamper.stamp(template, context, out); + var in = IOStreams.getInputStream(out); + document = WordprocessingMLPackage.load(in); + var visitor = newCollector(clazz); + getHeaderPart(document) + .ifPresent(hp -> TraversalUtil.visit(hp, visitor)); + TraversalUtil.visit(getMainPart(document), visitor); + getFooterPart(document) + .ifPresent(hp -> TraversalUtil.visit(hp, visitor)); + elements = visitor.elements(); + } catch (Docx4JException | IOException e) { + throw new RuntimeException(e); + } + return elements; + } - /** - * Stamps the given template resolving the expressions within the template against the specified context. - * Returns the resulting document after it has been saved and loaded - * again to ensure that changes in the DOCX4J - * object structure were really transported into the XML of the .docx file. - * - * @param template a {@link InputStream} object - * @param context a T object - * @return a {@link WordprocessingMLPackage} object - * @throws IOException if any. - * @throws Docx4JException if any. - * @since 1.6.6 - */ - public WordprocessingMLPackage stampAndLoad(InputStream template, T context) throws IOException, Docx4JException { - OutputStream out = IOStreams.getOutputStream(); - stamper.stamp(template, context, out); - InputStream in = IOStreams.getInputStream(out); - return WordprocessingMLPackage.load(in); - } + private List getMainPart(WordprocessingMLPackage document) { + return document.getMainDocumentPart() + .getContent(); + } - /** - *

stampAndLoadAndExtract.

- * - * @param template a {@link InputStream} object - * @param context a T object - * @return a {@link java.util.List} object - * @since 1.6.6 - */ - public String stampAndLoadAndExtract(InputStream template, T context) { - Stringifier stringifier = new Stringifier(() -> document); - return streamElements(template, context, P.class) - .map(stringifier::stringify) - .collect(joining("\n")); - } + private Optional getHeaderPart(WordprocessingMLPackage document) { + RelationshipsPart relPart = document.getMainDocumentPart() + .getRelationshipsPart(); + Relationship rel = relPart.getRelationshipByType(Namespaces.HEADER); + return Optional.ofNullable(rel) + .map(r -> (HeaderPart) relPart.getPart(r)); + } - private Stream streamElements(InputStream template, T context, Class clazz) { - Stream elements; - try { - var out = IOStreams.getOutputStream(); - stamper.stamp(template, context, out); - var in = IOStreams.getInputStream(out); - document = WordprocessingMLPackage.load(in); - var visitor = newCollector(clazz); - var mainDocumentPart = document.getMainDocumentPart(); - var content = mainDocumentPart.getContent(); - TraversalUtil.visit(content, visitor); - elements = visitor.elements(); - } catch (Docx4JException | IOException e) { - throw new RuntimeException(e); - } - return elements; - } + private Optional getFooterPart(WordprocessingMLPackage document) { + RelationshipsPart relPart = document.getMainDocumentPart() + .getRelationshipsPart(); + Relationship rel = relPart.getRelationshipByType(Namespaces.FOOTER); + return Optional.ofNullable(rel) + .map(r -> (FooterPart) relPart.getPart(r)); + } - private DocxCollector newCollector(Class type) { - return new DocxCollector<>(type); - } + private DocxCollector newCollector(Class type) { + return new DocxCollector<>(type); + } - /** - *

stampAndLoadAndExtract.

- * - * @param template a {@link InputStream} object - * @param context a T object - * @param clazz a {@link java.lang.Class} object - * @param a C class - * @return a {@link java.util.List} object - * @since 1.6.6 - */ - public List stampAndLoadAndExtract(InputStream template, T context, Class clazz) { - Stringifier stringifier = new Stringifier(() -> document); - return streamElements(template, context, clazz) - .map(stringifier::extractDocumentRuns) - .toList(); - } + /** + *

stampAndLoadAndExtract.

+ * + * @param template a {@link InputStream} object + * @param context a T object + * @param clazz a {@link java.lang.Class} object + * @param a C class + * @return a {@link java.util.List} object + * @since 1.6.6 + */ + public List stampAndLoadAndExtract( + InputStream template, + T context, + Class clazz + ) { + Stringifier stringifier = new Stringifier(() -> document); + return streamElements(template, context, clazz) + .map(stringifier::extractDocumentRuns) + .toList(); + } } From c645b9e280993309d37be31d2f983967793d89e0 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 14 Mar 2024 17:24:25 +0100 Subject: [PATCH 022/134] Refactor to simplify dependencies and exportation The 'pro.verron.docxstamper.core' package exportation has been removed for the 'module-info.java' to restrict its accessibility and promote code encapsulation. Meanwhile, to minimize coupling and uphold the Single Responsibility Principle, the 'findComment' method has been duplicated in the 'Stringifier.java' class, hence making any 'CommentUtil' class dependencies unnecessary for this class. --- src/main/java/module-info.java | 1 - .../docxstamper/test/utils/Stringifier.java | 30 +++++++++++++++++-- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 81c07b3a..7bfe257a 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,6 +1,5 @@ module pro.verron.opcstamper { exports pro.verron.docxstamper.api; - exports pro.verron.docxstamper.core; exports pro.verron.docxstamper.preset; exports org.wickedsource.docxstamper; diff --git a/src/test/java/pro/verron/docxstamper/test/utils/Stringifier.java b/src/test/java/pro/verron/docxstamper/test/utils/Stringifier.java index 632b489d..0f60d509 100644 --- a/src/test/java/pro/verron/docxstamper/test/utils/Stringifier.java +++ b/src/test/java/pro/verron/docxstamper/test/utils/Stringifier.java @@ -11,11 +11,13 @@ import org.docx4j.dml.wordprocessingDrawing.Inline; import org.docx4j.openpackaging.exceptions.Docx4JException; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; +import org.docx4j.openpackaging.parts.PartName; import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage; +import org.docx4j.openpackaging.parts.WordprocessingML.CommentsPart; import org.docx4j.wml.*; import org.wickedsource.docxstamper.api.DocxStamperException; -import pro.verron.docxstamper.core.CommentUtil; +import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.CharacterIterator; @@ -29,9 +31,9 @@ /** *

Stringifier class.

* - * @since 1.6.5 * @author Joseph Verron * @version ${version} + * @since 1.6.5 */ public class Stringifier { @@ -55,6 +57,28 @@ private static MessageDigest findDigest() { } } + /** + * Finds a comment with the given ID in the specified WordprocessingMLPackage document. + * + * @param document the WordprocessingMLPackage document to search for the comment + * @param id the ID of the comment to find + * @return an Optional containing the Comment if found, or an empty Optional if not found + * @throws Docx4JException if an error occurs while searching for the comment + */ + public static Optional findComment( + WordprocessingMLPackage document, BigInteger id + ) throws Docx4JException { + var name = new PartName("/word/comments.xml"); + var parts = document.getParts(); + var wordComments = (CommentsPart) parts.get(name); + var comments = wordComments.getContents(); + return comments.getComment() + .stream() + .filter(comment -> comment.getId() + .equals(id)) + .findFirst(); + } + private WordprocessingMLPackage document() { return documentSupplier.get(); } @@ -154,7 +178,7 @@ public String stringify(Object o) { return "|TAB|"; if (o instanceof R.CommentReference commentReference) { try { - return CommentUtil.findComment(document(), + return findComment(document(), commentReference.getId()) .map(c -> stringify(c.getContent())) .orElseThrow(); From 83a3e88bcb30e8174ed1f8a661c2ff9f10d50c06 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 14 Mar 2024 17:47:46 +0100 Subject: [PATCH 023/134] Exclude spring-jcl from spring-expression dependency Excluded the spring-jcl artifact from the spring-expression dependency within the project pom.xml file. This change helps resolve a module conflict issue, ensuring a more efficient and smooth flow of operations. --- pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pom.xml b/pom.xml index 10c74a5a..5fa46ad3 100644 --- a/pom.xml +++ b/pom.xml @@ -306,6 +306,12 @@ org.springframework spring-expression 6.1.4 + + + org.springframework + spring-jcl + + org.junit.jupiter From 2749e13462d71719e9a37774eee5beb6ba5f0532 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 14 Mar 2024 17:52:32 +0100 Subject: [PATCH 024/134] Update module dependencies in module-info.java Reorganized and updated required modules in module-info.java file. Changes include making dependencies for org.apache.commons.io, org.slf4j, and jakarta.xml.bind as static, and making org.docx4j.core as transitive. This optimizes the module system and enhances dependency resolution. --- src/main/java/module-info.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 7bfe257a..51aa39b6 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -10,11 +10,12 @@ exports org.wickedsource.docxstamper.processor; exports org.wickedsource.docxstamper.api; - - requires org.apache.commons.io; - requires org.docx4j.core; - requires spring.expression; - requires org.slf4j; requires spring.core; - requires jakarta.xml.bind; + requires spring.expression; + + requires transitive org.docx4j.core; + + requires static org.apache.commons.io; + requires static org.slf4j; + requires static jakarta.xml.bind; } From 4db7303dfceb800e2a2cdedd5f115210a9466048 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Fri, 15 Mar 2024 01:07:29 +0100 Subject: [PATCH 025/134] Refactor BaseCommentProcessor to extend AbstractCommentProcessor Migrated the majority of BaseCommentProcessor's functionality into a new class AbstractCommentProcessor. This allows better separation of core logic and implementation specifics. Adjustments to comply with this change have been made in related areas of the codebase including module-info and scheme tests. --- src/main/java/module-info.java | 2 +- .../processor/BaseCommentProcessor.java | 92 +---------------- .../api/AbstractCommentProcessor.java | 99 +++++++++++++++++++ .../CustomCommentProcessor.java | 4 +- 4 files changed, 106 insertions(+), 91 deletions(-) create mode 100644 src/main/java/pro/verron/docxstamper/api/AbstractCommentProcessor.java diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 51aa39b6..22e32984 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -7,7 +7,7 @@ exports org.wickedsource.docxstamper.processor.table; exports org.wickedsource.docxstamper.util; exports org.wickedsource.docxstamper.api.commentprocessor; - exports org.wickedsource.docxstamper.processor; + // exports org.wickedsource.docxstamper.processor; exports org.wickedsource.docxstamper.api; requires spring.core; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java index 96d458c4..a1f13591 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java @@ -1,15 +1,9 @@ package org.wickedsource.docxstamper.processor; -import org.docx4j.openpackaging.packages.WordprocessingMLPackage; -import org.docx4j.wml.P; -import org.docx4j.wml.R; import org.wickedsource.docxstamper.DocxStamper; -import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; -import pro.verron.docxstamper.api.CommentWrapper; +import pro.verron.docxstamper.api.AbstractCommentProcessor; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; -import java.util.Objects; - /** * Base class for comment processors. The current run and paragraph are set by the {@link DocxStamper} class. * @@ -18,17 +12,8 @@ * @version ${version} * @since 1.0.0 */ -public abstract class BaseCommentProcessor implements ICommentProcessor { - - /** - * PlaceholderReplacer used to replace expressions in the comment text. - */ - protected final ParagraphPlaceholderReplacer placeholderReplacer; - - private P paragraph; - private R currentRun; - private CommentWrapper currentCommentWrapper; - private WordprocessingMLPackage document; +public abstract class BaseCommentProcessor + extends AbstractCommentProcessor { /** *

Constructor for BaseCommentProcessor.

@@ -36,76 +21,7 @@ public abstract class BaseCommentProcessor implements ICommentProcessor { * @param placeholderReplacer PlaceholderReplacer used to replace placeholders in the comment text. */ protected BaseCommentProcessor(ParagraphPlaceholderReplacer placeholderReplacer) { - this.placeholderReplacer = placeholderReplacer; - } - - /** {@inheritDoc} */ - @Override - public void setCurrentRun(R run) { - this.currentRun = run; - } - - /** {@inheritDoc} */ - @Override - public void setParagraph(P paragraph) { - this.paragraph = paragraph; - } - - /** {@inheritDoc} */ - @Override - public void setCurrentCommentWrapper(CommentWrapper currentCommentWrapper) { - Objects.requireNonNull(currentCommentWrapper.getCommentRangeStart()); - Objects.requireNonNull(currentCommentWrapper.getCommentRangeEnd()); - this.currentCommentWrapper = currentCommentWrapper; - } - - /** - * {@inheritDoc} - * @deprecated the document is passed to the processor through the commitChange method now, - * and will probably pe passed through the constructor in the future - */ - @Deprecated(since = "1.6.5", forRemoval = true) - @Override - public void setDocument(WordprocessingMLPackage document) { - this.document = document; - } - - /** - *

Getter for the field currentCommentWrapper.

- * - * @return a {@link CommentWrapper} object - */ - public CommentWrapper getCurrentCommentWrapper() { - return currentCommentWrapper; + super(placeholderReplacer); } - /** - *

Getter for the field paragraph.

- * - * @return a {@link org.docx4j.wml.P} object - */ - public P getParagraph() { - return paragraph; - } - - /** - *

Getter for the field currentRun.

- * - * @return a {@link org.docx4j.wml.R} object - */ - public R getCurrentRun() { - return currentRun; - } - - /** - *

Getter for the field document.

- * - * @return a {@link WordprocessingMLPackage} object - * @deprecated the document is passed to the processor through the commitChange method now - * and will probably pe passed through the constructor in the future - */ - @Deprecated(since = "1.6.5", forRemoval = true) - public WordprocessingMLPackage getDocument() { - return document; - } } diff --git a/src/main/java/pro/verron/docxstamper/api/AbstractCommentProcessor.java b/src/main/java/pro/verron/docxstamper/api/AbstractCommentProcessor.java new file mode 100644 index 00000000..ce02cce4 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/api/AbstractCommentProcessor.java @@ -0,0 +1,99 @@ +package pro.verron.docxstamper.api; + +import org.docx4j.openpackaging.packages.WordprocessingMLPackage; +import org.docx4j.wml.P; +import org.docx4j.wml.R; +import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; + +import java.util.Objects; + +public abstract class AbstractCommentProcessor + implements ICommentProcessor { + /** + * PlaceholderReplacer used to replace expressions in the comment text. + */ + protected final ParagraphPlaceholderReplacer placeholderReplacer; + private P paragraph; + private R currentRun; + private CommentWrapper currentCommentWrapper; + private WordprocessingMLPackage document; + + public AbstractCommentProcessor(ParagraphPlaceholderReplacer placeholderReplacer) {this.placeholderReplacer = placeholderReplacer;} + + /** + *

Getter for the field currentCommentWrapper.

+ * + * @return a {@link CommentWrapper} object + */ + public CommentWrapper getCurrentCommentWrapper() { + return currentCommentWrapper; + } + + /** + * {@inheritDoc} + */ + @Override + public void setCurrentCommentWrapper(CommentWrapper currentCommentWrapper) { + Objects.requireNonNull(currentCommentWrapper.getCommentRangeStart()); + Objects.requireNonNull(currentCommentWrapper.getCommentRangeEnd()); + this.currentCommentWrapper = currentCommentWrapper; + } + + /** + *

Getter for the field paragraph.

+ * + * @return a {@link P} object + */ + public P getParagraph() { + return paragraph; + } + + /** + * {@inheritDoc} + */ + @Override + public void setParagraph(P paragraph) { + this.paragraph = paragraph; + } + + /** + *

Getter for the field currentRun.

+ * + * @return a {@link R} object + */ + public R getCurrentRun() { + return currentRun; + } + + /** + * {@inheritDoc} + */ + @Override + public void setCurrentRun(R run) { + this.currentRun = run; + } + + /** + *

Getter for the field document.

+ * + * @return a {@link WordprocessingMLPackage} object + * @deprecated the document is passed to the processor through the commitChange method now + * and will probably pe passed through the constructor in the future + */ + @Deprecated(since = "1.6.5", forRemoval = true) + public WordprocessingMLPackage getDocument() { + return document; + } + + /** + * {@inheritDoc} + * + * @deprecated the document is passed to the processor through the commitChange method now, + * and will probably pe passed through the constructor in the future + */ + @Deprecated(since = "1.6.5", forRemoval = true) + @Override + public void setDocument(WordprocessingMLPackage document) { + this.document = document; + } +} diff --git a/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java b/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java index c9ad745b..99e56695 100644 --- a/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java +++ b/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java @@ -4,8 +4,8 @@ import org.docx4j.wml.P; import org.docx4j.wml.R; import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; -import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.util.RunUtil; +import pro.verron.docxstamper.api.AbstractCommentProcessor; import pro.verron.docxstamper.api.CommentWrapper; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; @@ -29,7 +29,7 @@ * @since 1.6.6 */ public class CustomCommentProcessor - extends BaseCommentProcessor + extends AbstractCommentProcessor implements ICustomCommentProcessor { private static final List

visitedParagraphs = new ArrayList<>(); From 7ec273431932bb7c4105871fb3b37711907d7105 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Fri, 15 Mar 2024 01:30:51 +0100 Subject: [PATCH 026/134] Refactor 'getText()' method to 'asString()' The 'getText()' method in the Paragraph class and where it's called in other classes has been refactored to 'asString()'. Also, 'recalculateRuns()' and 'getRuns()' methods in Paragraph class have been made private, removing unnecessary public exposure. The 'getParagraph()' method from Paragraph class is also removed. --- .../processor/CommentProcessorRegistry.java | 2 +- .../verron/docxstamper/core/CommentUtil.java | 2 +- .../verron/docxstamper/core/Expressions.java | 2 +- .../verron/docxstamper/core/Paragraph.java | 21 ++++++------------- .../docxstamper/core/PlaceholderReplacer.java | 2 +- 5 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java index 58d10097..d7d90806 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java @@ -158,7 +158,7 @@ private void runProcessorsOnInlineContent( P paragraph ) { var paragraphWrapper = new Paragraph(paragraph); - String text = paragraphWrapper.getText(); + String text = paragraphWrapper.asString(); var expressions = Expressions.findProcessors(text); for (var expression : expressions) { diff --git a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java index 7a2a4fa5..9c3d0592 100644 --- a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java +++ b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java @@ -194,7 +194,7 @@ public static Placeholder getCommentString(Comment comment) { StringBuilder builder = new StringBuilder(); for (Object commentChildObject : comment.getContent()) { if (commentChildObject instanceof P p) { - builder.append(new Paragraph(p).getText()); + builder.append(new Paragraph(p).asString()); } } String string = builder.toString(); diff --git a/src/main/java/pro/verron/docxstamper/core/Expressions.java b/src/main/java/pro/verron/docxstamper/core/Expressions.java index 3ecd58f7..ffa0bcc9 100644 --- a/src/main/java/pro/verron/docxstamper/core/Expressions.java +++ b/src/main/java/pro/verron/docxstamper/core/Expressions.java @@ -76,7 +76,7 @@ public static List findVariables(String text) { } public static List findVariables(Paragraph paragraph) { - return findVariables(paragraph.getText()); + return findVariables(paragraph.asString()); } /** diff --git a/src/main/java/pro/verron/docxstamper/core/Paragraph.java b/src/main/java/pro/verron/docxstamper/core/Paragraph.java index ef9ba005..64b5681a 100644 --- a/src/main/java/pro/verron/docxstamper/core/Paragraph.java +++ b/src/main/java/pro/verron/docxstamper/core/Paragraph.java @@ -18,8 +18,7 @@ * runs a word or a string of words is spread.

*

This class aggregates multiple runs so they can be treated as a single text, no matter how many runs the text spans. * Call {@link #addRun(R, int)} to add all runs that should be aggregated. Then, call - * methods to modify the aggregated text. Finally, call {@link #getText()} or - * {@link #getRuns()} to get the modified text or the list of modified runs. + * methods to modify the aggregated text. Finally, call {@link #asString()} to get the modified text. * * @author Joseph Verron * @author Tom Hombergs @@ -45,7 +44,7 @@ public Paragraph(P paragraph) { * Recalculates the runs of the paragraph. This method is called automatically by the constructor, but can also be * called manually to recalculate the runs after a modification to the paragraph was done. */ - public void recalculateRuns() { + private void recalculateRuns() { currentPosition = 0; this.runs.clear(); int index = 0; @@ -80,7 +79,7 @@ private void addRun(R run, int index) { * @param replacement the object to replace the expression. */ public void replace(Placeholder placeholder, R replacement) { - String text = getText(); + String text = asString(); String full = placeholder.expression(); int matchStartIndex = text.indexOf(full); if (matchStartIndex == -1) { @@ -172,7 +171,7 @@ public void replace(Placeholder placeholder, R replacement) { * * @return the text of all runs. */ - public String getText() { + public String asString() { return runs.stream() .map(IndexedRun::run) .map(RunUtil::getText) @@ -191,7 +190,7 @@ private List getAffectedRuns(int startIndex, int endIndex) { * * @return the list of aggregated runs. */ - public List getRuns() { + private List getRuns() { return runs.stream() .map(IndexedRun::run) .toList(); @@ -202,15 +201,7 @@ public List getRuns() { */ @Override public String toString() { - return getText(); + return asString(); } - /** - *

Getter for the field paragraph.

- * - * @return a {@link P} object - */ - public P getParagraph() { - return paragraph; - } } diff --git a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java index e3b68bca..19cfa357 100644 --- a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java +++ b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java @@ -157,7 +157,7 @@ private void replaceLineBreaks(Paragraph paragraph) { Br lineBreak = Context.getWmlObjectFactory() .createBr(); R run = RunUtil.create(lineBreak); - while (paragraph.getText() + while (paragraph.asString() .contains(lineBreakPlaceholder().expression())) { paragraph.replace(lineBreakPlaceholder(), run); } From e897b484e360a7b74a1e522f28c0bc3d01501dfb Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Fri, 15 Mar 2024 01:31:06 +0100 Subject: [PATCH 027/134] Update module dependencies and open/export statements Updated the module dependencies in both the main and the test module-info.java files. Additionally, reorganized the open and export statements in these modules. The changes optimize the dependency hierarchy and improve the structure of module accessibility. --- src/main/java/module-info.java | 25 +++++++++++++++---------- src/test/java/module-info.java | 26 ++++++++++++++------------ 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 22e32984..0e705187 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,21 +1,26 @@ module pro.verron.opcstamper { + requires spring.core; + requires spring.expression; + + requires transitive org.docx4j.core; + + requires static org.apache.commons.io; + requires static org.slf4j; + requires static jakarta.xml.bind; + + opens pro.verron.docxstamper.api; + opens pro.verron.docxstamper.preset; + exports pro.verron.docxstamper.api; exports pro.verron.docxstamper.preset; exports org.wickedsource.docxstamper; + exports org.wickedsource.docxstamper.api; + exports org.wickedsource.docxstamper.api.commentprocessor; exports org.wickedsource.docxstamper.el; - exports org.wickedsource.docxstamper.processor.table; exports org.wickedsource.docxstamper.util; - exports org.wickedsource.docxstamper.api.commentprocessor; // exports org.wickedsource.docxstamper.processor; - exports org.wickedsource.docxstamper.api; - - requires spring.core; - requires spring.expression; + exports org.wickedsource.docxstamper.processor.table; - requires transitive org.docx4j.core; - requires static org.apache.commons.io; - requires static org.slf4j; - requires static jakarta.xml.bind; } diff --git a/src/test/java/module-info.java b/src/test/java/module-info.java index 9e491bb6..236761e1 100644 --- a/src/test/java/module-info.java +++ b/src/test/java/module-info.java @@ -1,23 +1,25 @@ module pro.verron.opcstamper.test { - requires pro.verron.opcstamper; + requires transitive pro.verron.opcstamper; + requires org.junit.jupiter.api; requires org.junit.jupiter.params; + + requires spring.core; requires spring.context; requires spring.expression; + requires org.docx4j.openxml_objects; requires org.docx4j.core; + requires org.slf4j; requires jakarta.xml.bind; - requires spring.core; - - opens pro.verron.docxstamper.test to - spring.expression, org.junit.platform.commons; - - opens org.wickedsource.docxstamper.test to org.junit.platform.commons; - opens pro.verron.docxstamper.test.utils.context to spring.core; - exports pro.verron.docxstamper.test.utils.context to spring.expression; - exports pro.verron.docxstamper.test.commentProcessors to pro.verron.opcstamper; - exports pro.verron.docxstamper.test to pro.verron.opcstamper; - exports org.wickedsource.docxstamper.test to spring.expression; + opens pro.verron.docxstamper.test; + opens pro.verron.docxstamper.test.utils.context; + opens org.wickedsource.docxstamper.test; + + exports org.wickedsource.docxstamper.test; + exports pro.verron.docxstamper.test; + exports pro.verron.docxstamper.test.commentProcessors; + exports pro.verron.docxstamper.test.utils.context; } From b1ebe1686cba919a2feb1fd9cc4aa6bd5cc8ead5 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Fri, 15 Mar 2024 01:35:38 +0100 Subject: [PATCH 028/134] Rename core.Paragraph to core.DefaultParagraph The Paragraph class in 'pro.verron.docxstamper.core' package has been renamed to DefaultParagraph and implemented as an interface in 'pro.verron.docxstamper.api' package. All existing references to the Paragraph class in other packages have been updated to reflect this change. As a result, the structure is more flexible for future extensions or alternative implementations of Paragraph handling. --- .../docxstamper/processor/CommentProcessorRegistry.java | 4 ++-- .../processor/repeat/ParagraphRepeatProcessor.java | 4 ++-- .../repeat/ParagraphResolverDocumentWalker.java | 4 ++-- src/main/java/pro/verron/docxstamper/api/Paragraph.java | 9 +++++++++ .../docxstamper/api/ParagraphPlaceholderReplacer.java | 1 - .../java/pro/verron/docxstamper/core/CommentUtil.java | 2 +- .../core/{Paragraph.java => DefaultParagraph.java} | 8 ++++++-- .../java/pro/verron/docxstamper/core/Expressions.java | 1 + .../pro/verron/docxstamper/core/PlaceholderReplacer.java | 3 ++- 9 files changed, 25 insertions(+), 11 deletions(-) create mode 100644 src/main/java/pro/verron/docxstamper/api/Paragraph.java rename src/main/java/pro/verron/docxstamper/core/{Paragraph.java => DefaultParagraph.java} (97%) diff --git a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java index d7d90806..b37f319c 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java @@ -17,8 +17,8 @@ import org.wickedsource.docxstamper.util.walk.BaseCoordinatesWalker; import pro.verron.docxstamper.api.CommentWrapper; import pro.verron.docxstamper.core.CommentUtil; +import pro.verron.docxstamper.core.DefaultParagraph; import pro.verron.docxstamper.core.Expressions; -import pro.verron.docxstamper.core.Paragraph; import java.math.BigInteger; import java.util.ArrayList; @@ -157,7 +157,7 @@ private void runProcessorsOnInlineContent( T expressionContext, P paragraph ) { - var paragraphWrapper = new Paragraph(paragraph); + var paragraphWrapper = new DefaultParagraph(paragraph); String text = paragraphWrapper.asString(); var expressions = Expressions.findProcessors(text); diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java index 1a64812d..e6b3aa2c 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java @@ -11,7 +11,7 @@ import pro.verron.docxstamper.api.CommentWrapper; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.core.CommentUtil; -import pro.verron.docxstamper.core.Paragraph; +import pro.verron.docxstamper.core.DefaultParagraph; import pro.verron.docxstamper.core.PlaceholderReplacer; import java.math.BigInteger; @@ -201,7 +201,7 @@ private Deque

generateParagraphsToAdd( paragraphs.commentWrapper.getComment() .getId()); placeholderReplacer.resolveExpressionsForParagraph( - new Paragraph(pClone), + new DefaultParagraph(pClone), expressionContext, document ); diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java index 509bf73b..cb0587c1 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java @@ -5,7 +5,7 @@ import org.docx4j.wml.Tr; import org.wickedsource.docxstamper.util.walk.BaseDocumentWalker; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; -import pro.verron.docxstamper.core.Paragraph; +import pro.verron.docxstamper.core.DefaultParagraph; /** * Walks through a document and replaces expressions with values from the given @@ -48,7 +48,7 @@ public ParagraphResolverDocumentWalker( @Override protected void onParagraph(P paragraph) { placeholderReplacer.resolveExpressionsForParagraph( - new Paragraph(paragraph), + new DefaultParagraph(paragraph), expressionContext, document ); } diff --git a/src/main/java/pro/verron/docxstamper/api/Paragraph.java b/src/main/java/pro/verron/docxstamper/api/Paragraph.java new file mode 100644 index 00000000..ebe7a06e --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/api/Paragraph.java @@ -0,0 +1,9 @@ +package pro.verron.docxstamper.api; + +import org.docx4j.wml.R; + +public interface Paragraph { + void replace(Placeholder placeholder, R replacement); + + String asString(); +} diff --git a/src/main/java/pro/verron/docxstamper/api/ParagraphPlaceholderReplacer.java b/src/main/java/pro/verron/docxstamper/api/ParagraphPlaceholderReplacer.java index 4e9f7fee..b69b3a31 100644 --- a/src/main/java/pro/verron/docxstamper/api/ParagraphPlaceholderReplacer.java +++ b/src/main/java/pro/verron/docxstamper/api/ParagraphPlaceholderReplacer.java @@ -1,7 +1,6 @@ package pro.verron.docxstamper.api; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; -import pro.verron.docxstamper.core.Paragraph; public interface ParagraphPlaceholderReplacer { void resolveExpressionsForParagraph( diff --git a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java index 9c3d0592..6e787e1b 100644 --- a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java +++ b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java @@ -194,7 +194,7 @@ public static Placeholder getCommentString(Comment comment) { StringBuilder builder = new StringBuilder(); for (Object commentChildObject : comment.getContent()) { if (commentChildObject instanceof P p) { - builder.append(new Paragraph(p).asString()); + builder.append(new DefaultParagraph(p).asString()); } } String string = builder.toString(); diff --git a/src/main/java/pro/verron/docxstamper/core/Paragraph.java b/src/main/java/pro/verron/docxstamper/core/DefaultParagraph.java similarity index 97% rename from src/main/java/pro/verron/docxstamper/core/Paragraph.java rename to src/main/java/pro/verron/docxstamper/core/DefaultParagraph.java index 64b5681a..4cda56e1 100644 --- a/src/main/java/pro/verron/docxstamper/core/Paragraph.java +++ b/src/main/java/pro/verron/docxstamper/core/DefaultParagraph.java @@ -4,6 +4,7 @@ import org.docx4j.wml.R; import org.wickedsource.docxstamper.util.IndexedRun; import org.wickedsource.docxstamper.util.RunUtil; +import pro.verron.docxstamper.api.Paragraph; import pro.verron.docxstamper.api.Placeholder; import java.util.ArrayList; @@ -25,7 +26,8 @@ * @version ${version} * @since 1.0.8 */ -public class Paragraph { +public class DefaultParagraph + implements Paragraph { private final List runs = new ArrayList<>(); private final P paragraph; private int currentPosition = 0; @@ -35,7 +37,7 @@ public class Paragraph { * * @param paragraph the paragraph to wrap. */ - public Paragraph(P paragraph) { + public DefaultParagraph(P paragraph) { this.paragraph = paragraph; recalculateRuns(); } @@ -78,6 +80,7 @@ private void addRun(R run, int index) { * @param placeholder the expression to be replaced. * @param replacement the object to replace the expression. */ + @Override public void replace(Placeholder placeholder, R replacement) { String text = asString(); String full = placeholder.expression(); @@ -171,6 +174,7 @@ public void replace(Placeholder placeholder, R replacement) { * * @return the text of all runs. */ + @Override public String asString() { return runs.stream() .map(IndexedRun::run) diff --git a/src/main/java/pro/verron/docxstamper/core/Expressions.java b/src/main/java/pro/verron/docxstamper/core/Expressions.java index ffa0bcc9..94761e37 100644 --- a/src/main/java/pro/verron/docxstamper/core/Expressions.java +++ b/src/main/java/pro/verron/docxstamper/core/Expressions.java @@ -1,6 +1,7 @@ package pro.verron.docxstamper.core; import org.wickedsource.docxstamper.api.DocxStamperException; +import pro.verron.docxstamper.api.Paragraph; import pro.verron.docxstamper.api.Placeholder; import pro.verron.docxstamper.core.expression.ExpressionFinder; import pro.verron.docxstamper.core.expression.Matcher; diff --git a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java index 19cfa357..621d0657 100644 --- a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java +++ b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java @@ -13,6 +13,7 @@ import org.wickedsource.docxstamper.el.ExpressionResolver; import org.wickedsource.docxstamper.util.RunUtil; import org.wickedsource.docxstamper.util.walk.BaseCoordinatesWalker; +import pro.verron.docxstamper.api.Paragraph; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.api.Placeholder; @@ -87,7 +88,7 @@ public void resolveExpressions( @Override protected void onParagraph(P paragraph) { resolveExpressionsForParagraph( - new Paragraph(paragraph), + new DefaultParagraph(paragraph), expressionContext, document); } From 2abb02019e89419217b326bd98221465d0181cb9 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Fri, 15 Mar 2024 02:29:41 +0100 Subject: [PATCH 029/134] Refactor EvaluationContextConfigurer usage Removed direct import of NoOpEvaluationContextConfigurer and replaced its instances with new utility class EvaluationContextConfigurers. This makes the instantiation of NoOpEvaluationContextConfigurer more streamlined and avoids direct dependency on the NoOpEvaluationContextConfigurer class in the DefaultTests class. The org.wickedsource.docxstamper.el package is no longer exposed in the module-info. --- src/main/java/module-info.java | 2 +- .../preset/EvaluationContextConfigurers.java | 10 ++++++++++ .../wickedsource/docxstamper/test/DefaultTests.java | 8 ++++---- 3 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 src/main/java/pro/verron/docxstamper/preset/EvaluationContextConfigurers.java diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 0e705187..aa1a1a06 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -17,7 +17,7 @@ exports org.wickedsource.docxstamper; exports org.wickedsource.docxstamper.api; exports org.wickedsource.docxstamper.api.commentprocessor; - exports org.wickedsource.docxstamper.el; + // exports org.wickedsource.docxstamper.el; exports org.wickedsource.docxstamper.util; // exports org.wickedsource.docxstamper.processor; exports org.wickedsource.docxstamper.processor.table; diff --git a/src/main/java/pro/verron/docxstamper/preset/EvaluationContextConfigurers.java b/src/main/java/pro/verron/docxstamper/preset/EvaluationContextConfigurers.java new file mode 100644 index 00000000..18f3fd51 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/preset/EvaluationContextConfigurers.java @@ -0,0 +1,10 @@ +package pro.verron.docxstamper.preset; + +import org.wickedsource.docxstamper.api.EvaluationContextConfigurer; +import org.wickedsource.docxstamper.el.NoOpEvaluationContextConfigurer; + +public class EvaluationContextConfigurers { + public static EvaluationContextConfigurer noopConfigurer() { + return new NoOpEvaluationContextConfigurer(); + } +} diff --git a/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java b/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java index ecacc1b1..7dd0cb75 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java +++ b/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java @@ -8,8 +8,8 @@ import org.springframework.expression.spel.SpelParserConfiguration; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.wickedsource.docxstamper.DocxStamperConfiguration; -import org.wickedsource.docxstamper.el.NoOpEvaluationContextConfigurer; import pro.verron.docxstamper.api.Image; +import pro.verron.docxstamper.preset.EvaluationContextConfigurers; import pro.verron.docxstamper.preset.Resolvers; import pro.verron.docxstamper.test.Functions; import pro.verron.docxstamper.test.accessors.SimpleGetter; @@ -1173,9 +1173,9 @@ private static Arguments nullPointerResolutionTest_testWithCustomSpel() { // so it will not work if your type has no default constructor and no setters. var config = new DocxStamperConfiguration() - .setSpelParserConfiguration( - new SpelParserConfiguration(true, true)) - .setEvaluationContextConfigurer(new NoOpEvaluationContextConfigurer()) + .setSpelParserConfiguration(new SpelParserConfiguration(true, + true)) + .setEvaluationContextConfigurer(EvaluationContextConfigurers.noopConfigurer()) .addResolver(Resolvers.nullToDefault("Nullish value!!")); return arguments("nullPointerResolutionTest_testWithCustomSpel", From 160f5db98517fd163ff5953b82be26874aafe97b Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Fri, 15 Mar 2024 05:45:02 +0100 Subject: [PATCH 030/134] Update StampTable package and adjust imports Moved StampTable from org.wickedsource.docxstamper.processor.table to pro.verron.docxstamper.api, and updated all related imports across multiple files. This architectural refactoring helps keep code related to the StampTable more organized. Made the corresponding change in module-info.java to stop exposing the previous package. --- src/main/java/module-info.java | 2 +- .../docxstamper/processor/table/ITableResolver.java | 2 ++ .../wickedsource/docxstamper/processor/table/TableResolver.java | 1 + .../table => pro/verron/docxstamper/api}/StampTable.java | 2 +- .../pro/verron/docxstamper/test/utils/context/Contexts.java | 2 +- 5 files changed, 6 insertions(+), 3 deletions(-) rename src/main/java/{org/wickedsource/docxstamper/processor/table => pro/verron/docxstamper/api}/StampTable.java (96%) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index aa1a1a06..87d4f121 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -20,7 +20,7 @@ // exports org.wickedsource.docxstamper.el; exports org.wickedsource.docxstamper.util; // exports org.wickedsource.docxstamper.processor; - exports org.wickedsource.docxstamper.processor.table; + // exports org.wickedsource.docxstamper.processor.table; } diff --git a/src/main/java/org/wickedsource/docxstamper/processor/table/ITableResolver.java b/src/main/java/org/wickedsource/docxstamper/processor/table/ITableResolver.java index a17f020e..8981c640 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/table/ITableResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/table/ITableResolver.java @@ -1,5 +1,7 @@ package org.wickedsource.docxstamper.processor.table; +import pro.verron.docxstamper.api.StampTable; + /** * This interface is used to resolve a table in the template document. * The table is passed to the resolveTable method and will be used to fill an existing Tbl object in the document. diff --git a/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java b/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java index db7057ad..67e9dcb5 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java @@ -9,6 +9,7 @@ import org.wickedsource.docxstamper.processor.CommentProcessingException; import org.wickedsource.docxstamper.util.ParagraphUtil; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; +import pro.verron.docxstamper.api.StampTable; import pro.verron.docxstamper.core.PlaceholderReplacer; import java.util.Collections; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java b/src/main/java/pro/verron/docxstamper/api/StampTable.java similarity index 96% rename from src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java rename to src/main/java/pro/verron/docxstamper/api/StampTable.java index 0a041310..2c52002a 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java +++ b/src/main/java/pro/verron/docxstamper/api/StampTable.java @@ -1,4 +1,4 @@ -package org.wickedsource.docxstamper.processor.table; +package pro.verron.docxstamper.api; import org.springframework.lang.NonNull; diff --git a/src/test/java/pro/verron/docxstamper/test/utils/context/Contexts.java b/src/test/java/pro/verron/docxstamper/test/utils/context/Contexts.java index 28fc1c4c..557edddc 100644 --- a/src/test/java/pro/verron/docxstamper/test/utils/context/Contexts.java +++ b/src/test/java/pro/verron/docxstamper/test/utils/context/Contexts.java @@ -1,7 +1,7 @@ package pro.verron.docxstamper.test.utils.context; -import org.wickedsource.docxstamper.processor.table.StampTable; import pro.verron.docxstamper.api.Image; +import pro.verron.docxstamper.api.StampTable; import java.util.*; From a48742f0abd6d61c8b3e58106629c52f18322862 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Fri, 15 Mar 2024 06:03:49 +0100 Subject: [PATCH 031/134] Remove test classes and refactor utility usage Deleted two unused test classes (`IndexedRunTest` and `RunUtilTest`) and revised code that relies on utility classes. Applied cleaner implementation of utility methods in `CustomCommentProcessor` and `MultiSectionTest`. Updated module-info.java files to reflect these changes. --- src/main/java/module-info.java | 2 +- .../docxstamper/util/DocumentUtil.java | 408 ++++++++++-------- src/test/java/module-info.java | 11 +- .../docxstamper/test/IndexedRunTest.java | 66 --- .../docxstamper/test/MultiSectionTest.java | 42 +- .../docxstamper/test/RunUtilTest.java | 61 --- .../CustomCommentProcessor.java | 17 +- 7 files changed, 261 insertions(+), 346 deletions(-) delete mode 100644 src/test/java/org/wickedsource/docxstamper/test/IndexedRunTest.java delete mode 100644 src/test/java/org/wickedsource/docxstamper/test/RunUtilTest.java diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 87d4f121..8002339b 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -18,7 +18,7 @@ exports org.wickedsource.docxstamper.api; exports org.wickedsource.docxstamper.api.commentprocessor; // exports org.wickedsource.docxstamper.el; - exports org.wickedsource.docxstamper.util; + // exports org.wickedsource.docxstamper.util; // exports org.wickedsource.docxstamper.processor; // exports org.wickedsource.docxstamper.processor.table; diff --git a/src/main/java/org/wickedsource/docxstamper/util/DocumentUtil.java b/src/main/java/org/wickedsource/docxstamper/util/DocumentUtil.java index 1685abbd..d97c2db5 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/DocumentUtil.java +++ b/src/main/java/org/wickedsource/docxstamper/util/DocumentUtil.java @@ -22,193 +22,227 @@ * @since 1.4.7 */ public class DocumentUtil { - private DocumentUtil() { - throw new DocxStamperException("Utility classes shouldn't be instantiated"); - } - - /** - * Retrieve all the paragraphs from a document - * - * @param parentObject the document to get the paragraphs from - * @return a list of paragraphs - */ - public static List

getParagraphsFromObject(Object parentObject) { - return streamElements(parentObject, P.class).toList(); - } - - /** - * Retrieve all the elements of a given class from an object. - * - * @param object the object to get the elements from - * @param elementClass the class of the elements to get - * @param the type of the elements to get - * @return a stream of the elements - */ - public static Stream streamElements(Object object, Class elementClass) { - return object instanceof WordprocessingMLPackage document - ? streamDocumentElements(document, elementClass) - : streamObjectElements(object, elementClass); - } - - /** - * we handle full documents slightly differently as they have headers and footers, - * and we want to get all the elements from them as well - * - * @param document the document to get the elements from - * @param elementClass the class of the elements to get - * @param the type of the elements to get - * @return a stream of the elements - */ - private static Stream streamDocumentElements(WordprocessingMLPackage document, Class elementClass) { - RelationshipsPart mainParts = document.getMainDocumentPart().getRelationshipsPart(); - return Stream.of( - streamElements(mainParts, Namespaces.HEADER, elementClass), - streamObjectElements(document.getMainDocumentPart(), elementClass), - streamElements(mainParts, Namespaces.FOOTER, elementClass) - ) - .reduce(Stream.empty(), Stream::concat); - } - - private static Stream streamObjectElements(Object obj, Class elementClass) { - ClassFinder finder = new ClassFinder(elementClass); - TraversalUtil.visit(obj, finder); - return finder.results.stream().map(elementClass::cast); - } - - private static Stream streamElements(RelationshipsPart mainParts, String namespace, Class elementClass) { - return mainParts - .getRelationshipsByType(namespace).stream() - .map(mainParts::getPart) - .flatMap(part -> streamObjectElements(part, elementClass)); - } - - /** - * Retrieve all the tables from an object. - * - * @param parentObject the object to get the tables from - * @return a list of tables - */ - public static List getTableFromObject(Object parentObject) { - return streamElements(parentObject, Tbl.class).toList(); - } - - /** - * Retrieve all the rows from an object. - * - * @param parentObject the object to get the rows from - * @return a list of rows - */ - public static List getTableRowsFromObject(Object parentObject) { - return streamElements(parentObject, Tr.class).toList(); - } - - /** - * Retrieve all the cells from an object. - * - * @param parentObject the object to get the cells from - * @return a list of cells - */ - public static List getTableCellsFromObject(Object parentObject) { - return streamElements(parentObject, Tc.class).toList(); - } - - /** - * Retrieve the first element from an object. - * - * @param subDocument the object to get the first element from - * @return the first element - */ - public static Object lastElement(WordprocessingMLPackage subDocument) { - return new ArrayDeque<>(subDocument.getMainDocumentPart().getContent()).getLast(); - } - - /** - * Retrieve the last element from an object. - * - * @param subDocument the object to get the last element from - * @return the last element - */ - public static List allElements(WordprocessingMLPackage subDocument) { - return subDocument.getMainDocumentPart().getContent(); - } - - /** - * Recursively walk through a source to find embedded images and import them in the target document. - * - * @param source source document containing image files. - * @param target target document to add image files to. - * @return a {@link java.util.Map} object - */ - public static Map walkObjectsAndImportImages(WordprocessingMLPackage source, WordprocessingMLPackage target) { - return walkObjectsAndImportImages(source.getMainDocumentPart(), source, target); - } - - /** - * Recursively walk through source accessor to find embedded images and import the target document. - * - * @param container source container to walk. - * @param source source document containing image files. - * @param target target document to add image files to. - * @return a {@link java.util.Map} object - */ - public static Map walkObjectsAndImportImages( - ContentAccessor container, - WordprocessingMLPackage source, - WordprocessingMLPackage target - ) { - Map replacements = new HashMap<>(); - for (Object obj : container.getContent()) { - Queue queue = new ArrayDeque<>(); - queue.add(obj); - - while (!queue.isEmpty()) { - Object currentObj = queue.remove(); - - if (currentObj instanceof R currentR && isImageRun(currentR)) { - DocxImageExtractor docxImageExtractor = new DocxImageExtractor(source); - byte[] imageData = docxImageExtractor.getRunDrawingData(currentR); - Integer maxWidth = docxImageExtractor.getRunDrawingMaxWidth(currentR); - BinaryPartAbstractImage imagePart = tryCreateImagePart(target, imageData); - replacements.put(currentR, - RunUtil.createRunWithImage(maxWidth, - imagePart)); - } else if (currentObj instanceof ContentAccessor contentAccessor) - queue.addAll(contentAccessor.getContent()); - } - } - return replacements; - } - - /** - * Check if a run contains an embedded image. - * - * @param run the run to analyze - * @return true if the run contains an image, false otherwise. - */ - private static boolean isImageRun(R run) { - return run.getContent() - .stream() + private DocumentUtil() { + throw new DocxStamperException( + "Utility classes shouldn't be instantiated"); + } + + /** + * Retrieve all the paragraphs from a document + * + * @param parentObject the document to get the paragraphs from + * @return a list of paragraphs + */ + public static List

getParagraphsFromObject(Object parentObject) { + return streamElements(parentObject, P.class).toList(); + } + + /** + * Retrieve all the elements of a given class from an object. + * + * @param object the object to get the elements from + * @param elementClass the class of the elements to get + * @param the type of the elements to get + * @return a stream of the elements + */ + public static Stream streamElements( + Object object, + Class elementClass + ) { + return object instanceof WordprocessingMLPackage document + ? streamDocumentElements(document, elementClass) + : streamObjectElements(object, elementClass); + } + + /** + * we handle full documents slightly differently as they have headers and footers, + * and we want to get all the elements from them as well + * + * @param document the document to get the elements from + * @param elementClass the class of the elements to get + * @param the type of the elements to get + * @return a stream of the elements + */ + private static Stream streamDocumentElements( + WordprocessingMLPackage document, + Class elementClass + ) { + RelationshipsPart mainParts = document.getMainDocumentPart() + .getRelationshipsPart(); + return Stream.of( + streamElements(mainParts, Namespaces.HEADER, elementClass), + streamObjectElements(document.getMainDocumentPart(), + elementClass), + streamElements(mainParts, Namespaces.FOOTER, elementClass) + ) + .reduce(Stream.empty(), Stream::concat); + } + + private static Stream streamObjectElements( + Object obj, + Class elementClass + ) { + ClassFinder finder = new ClassFinder(elementClass); + TraversalUtil.visit(obj, finder); + return finder.results.stream() + .map(elementClass::cast); + } + + private static Stream streamElements( + RelationshipsPart mainParts, + String namespace, + Class elementClass + ) { + return mainParts + .getRelationshipsByType(namespace) + .stream() + .map(mainParts::getPart) + .flatMap(part -> streamObjectElements(part, elementClass)); + } + + /** + * Retrieve all the tables from an object. + * + * @param parentObject the object to get the tables from + * @return a list of tables + */ + public static List getTableFromObject(Object parentObject) { + return streamElements(parentObject, Tbl.class).toList(); + } + + /** + * Retrieve all the rows from an object. + * + * @param parentObject the object to get the rows from + * @return a list of rows + */ + public static List getTableRowsFromObject(Object parentObject) { + return streamElements(parentObject, Tr.class).toList(); + } + + /** + * Retrieve all the cells from an object. + * + * @param parentObject the object to get the cells from + * @return a list of cells + */ + public static List getTableCellsFromObject(Object parentObject) { + return streamElements(parentObject, Tc.class).toList(); + } + + /** + * Retrieve the first element from an object. + * + * @param subDocument the object to get the first element from + * @return the first element + */ + public static Object lastElement(WordprocessingMLPackage subDocument) { + return new ArrayDeque<>(subDocument.getMainDocumentPart() + .getContent()).getLast(); + } + + /** + * Retrieve the last element from an object. + * + * @param subDocument the object to get the last element from + * @return the last element + */ + public static List allElements(WordprocessingMLPackage subDocument) { + return subDocument.getMainDocumentPart() + .getContent(); + } + + /** + * Recursively walk through a source to find embedded images and import them in the target document. + * + * @param source source document containing image files. + * @param target target document to add image files to. + * @return a {@link java.util.Map} object + */ + public static Map walkObjectsAndImportImages( + WordprocessingMLPackage source, + WordprocessingMLPackage target + ) { + return walkObjectsAndImportImages(source.getMainDocumentPart(), + source, + target); + } + + /** + * Recursively walk through source accessor to find embedded images and import the target document. + * + * @param container source container to walk. + * @param source source document containing image files. + * @param target target document to add image files to. + * @return a {@link java.util.Map} object + */ + public static Map walkObjectsAndImportImages( + ContentAccessor container, + WordprocessingMLPackage source, + WordprocessingMLPackage target + ) { + Map replacements = new HashMap<>(); + for (Object obj : container.getContent()) { + Queue queue = new ArrayDeque<>(); + queue.add(obj); + + while (!queue.isEmpty()) { + Object currentObj = queue.remove(); + + if (currentObj instanceof R currentR && isImageRun(currentR)) { + DocxImageExtractor docxImageExtractor = new DocxImageExtractor( + source); + byte[] imageData = docxImageExtractor.getRunDrawingData( + currentR); + Integer maxWidth = docxImageExtractor.getRunDrawingMaxWidth( + currentR); + BinaryPartAbstractImage imagePart = tryCreateImagePart( + target, + imageData); + R runWithImage = RunUtil.createRunWithImage(maxWidth, + imagePart); + replacements.put(currentR, runWithImage); + } else if (currentObj instanceof ContentAccessor contentAccessor) + queue.addAll(contentAccessor.getContent()); + } + } + return replacements; + } + + /** + * Check if a run contains an embedded image. + * + * @param run the run to analyze + * @return true if the run contains an image, false otherwise. + */ + private static boolean isImageRun(R run) { + return run.getContent() + .stream() .filter(JAXBElement.class::isInstance) - .map(JAXBElement.class::cast) - .map(JAXBElement::getValue) + .map(JAXBElement.class::cast) + .map(JAXBElement::getValue) .anyMatch(Drawing.class::isInstance); - } - - private static BinaryPartAbstractImage tryCreateImagePart(WordprocessingMLPackage destDocument, byte[] imageData) { - try { - return BinaryPartAbstractImage.createImagePart(destDocument, imageData); - } catch (Exception e) { - throw new DocxStamperException(e); - } - } - - /** - * Retrieve all the runs from a document. - * - * @param document the document to get the runs from - * @return the runs - */ - public static Stream

streamParagraphs(WordprocessingMLPackage document) { - return streamElements(document, P.class); - } + } + + private static BinaryPartAbstractImage tryCreateImagePart( + WordprocessingMLPackage destDocument, + byte[] imageData + ) { + try { + return BinaryPartAbstractImage.createImagePart(destDocument, + imageData); + } catch (Exception e) { + throw new DocxStamperException(e); + } + } + + /** + * Retrieve all the runs from a document. + * + * @param document the document to get the runs from + * @return the runs + */ + public static Stream

streamParagraphs(WordprocessingMLPackage document) { + return streamElements(document, P.class); + } } diff --git a/src/test/java/module-info.java b/src/test/java/module-info.java index 236761e1..f43dc1cb 100644 --- a/src/test/java/module-info.java +++ b/src/test/java/module-info.java @@ -15,11 +15,12 @@ requires jakarta.xml.bind; opens pro.verron.docxstamper.test; + opens pro.verron.docxstamper.test.commentProcessors; opens pro.verron.docxstamper.test.utils.context; opens org.wickedsource.docxstamper.test; - - exports org.wickedsource.docxstamper.test; - exports pro.verron.docxstamper.test; - exports pro.verron.docxstamper.test.commentProcessors; - exports pro.verron.docxstamper.test.utils.context; + + // exports pro.verron.docxstamper.test; + // exports pro.verron.docxstamper.test.commentProcessors; + // exports pro.verron.docxstamper.test.utils.context; + // exports org.wickedsource.docxstamper.test; } diff --git a/src/test/java/org/wickedsource/docxstamper/test/IndexedRunTest.java b/src/test/java/org/wickedsource/docxstamper/test/IndexedRunTest.java deleted file mode 100644 index 73455392..00000000 --- a/src/test/java/org/wickedsource/docxstamper/test/IndexedRunTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.wickedsource.docxstamper.test; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.wickedsource.docxstamper.util.IndexedRun; -import org.wickedsource.docxstamper.util.RunUtil; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * @author Joseph Verron - * @author Tom Hombergs - */ -@DisplayName("Utilities - Indexed Run") -class IndexedRunTest { - - @Test - void isNotTouchedByRangeBeforeStart() { - IndexedRun run = new IndexedRun(5, 10, 0, null); - assertFalse(run.isTouchedByRange(0, 4)); - } - - @Test - void isNotTouchedByRangeAfterEnd() { - IndexedRun run = new IndexedRun(5, 10, 0, null); - assertFalse(run.isTouchedByRange(11, 15)); - } - - @Test - void isTouchedByRangeEndingAtStart() { - IndexedRun run = new IndexedRun(5, 10, 0, null); - assertTrue(run.isTouchedByRange(0, 5)); - assertTrue(run.isTouchedByRange(4, 5)); - } - - @Test - void isTouchedByRangeEndingAtEnd() { - IndexedRun run = new IndexedRun(5, 10, 0, null); - assertTrue(run.isTouchedByRange(6, 10)); - assertTrue(run.isTouchedByRange(9, 10)); - } - - @Test - void isTouchedByRangeWithin() { - IndexedRun run = new IndexedRun(5, 10, 0, null); - assertTrue(run.isTouchedByRange(5, 7)); - } - - @Test - void isTouchedByRangeBeforeStartAndAfterEnd() { - IndexedRun run = new IndexedRun(5, 10, 0, null); - assertTrue(run.isTouchedByRange(4, 11)); - assertTrue(run.isTouchedByRange(0, 15)); - } - - @Test - void replaceWorksWithinRange() { - IndexedRun run = new IndexedRun(5, 9, 0, RunUtil.create("ipsum")); - run.replace(5, 9, "lorem"); - assertEquals("lorem", RunUtil.getText(run.run())); - run.replace(8, 9, "el"); - assertEquals("lorel", RunUtil.getText(run.run())); - run.replace(8, 9, "em ipsum"); - assertEquals("lorem ipsum", RunUtil.getText(run.run())); - } -} diff --git a/src/test/java/org/wickedsource/docxstamper/test/MultiSectionTest.java b/src/test/java/org/wickedsource/docxstamper/test/MultiSectionTest.java index 8748c573..cad5ba8a 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/MultiSectionTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/MultiSectionTest.java @@ -1,19 +1,12 @@ package org.wickedsource.docxstamper.test; -import org.docx4j.TextUtils; -import org.docx4j.openpackaging.exceptions.Docx4JException; -import org.docx4j.openpackaging.packages.WordprocessingMLPackage; -import org.docx4j.wml.R; import org.junit.jupiter.api.Test; import org.wickedsource.docxstamper.DocxStamperConfiguration; -import org.wickedsource.docxstamper.util.DocumentUtil; import pro.verron.docxstamper.test.utils.TestDocxStamper; -import java.io.IOException; import java.nio.file.Path; -import java.util.List; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.wickedsource.docxstamper.test.DefaultTests.getResource; /** @@ -21,22 +14,23 @@ */ class MultiSectionTest { - @Test - void expressionsInMultipleSections() throws Docx4JException, IOException { - var context = new NamesContext("Homer", "Marge"); - var template = getResource(Path.of("MultiSectionTest.docx")); - var stamper = new TestDocxStamper( - new DocxStamperConfiguration()); - var document = stamper.stampAndLoad(template, context); - assertTableRows(document); - } + @Test + void expressionsInMultipleSections() { + var context = new NamesContext("Homer", "Marge"); + var template = getResource(Path.of("MultiSectionTest.docx")); + var stamper = new TestDocxStamper( + new DocxStamperConfiguration()); + var actual = stamper.stampAndLoadAndExtract(template, context); + String expected = """ + Homer + + ❬❘docGrid=xxx,eGHdrFtrReferences=xxx,pgMar=xxx,pgSz={h=16838,w=11906}❭ + Marge"""; + assertEquals(expected, + actual); + } - private void assertTableRows(WordprocessingMLPackage document) { - final List runs = DocumentUtil.streamElements(document, R.class).toList(); - assertTrue(runs.stream().map(TextUtils::getText).anyMatch(s -> s.contains("Homer"))); - assertTrue(runs.stream().map(TextUtils::getText).anyMatch(s -> s.contains("Marge"))); - } - public record NamesContext(String firstName, String secondName) { - } + public record NamesContext(String firstName, String secondName) { + } } diff --git a/src/test/java/org/wickedsource/docxstamper/test/RunUtilTest.java b/src/test/java/org/wickedsource/docxstamper/test/RunUtilTest.java deleted file mode 100644 index 54862833..00000000 --- a/src/test/java/org/wickedsource/docxstamper/test/RunUtilTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.wickedsource.docxstamper.test; - -import org.docx4j.openpackaging.exceptions.Docx4JException; -import org.docx4j.openpackaging.packages.WordprocessingMLPackage; -import org.docx4j.wml.P; -import org.docx4j.wml.R; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.wickedsource.docxstamper.util.RunUtil; -import pro.verron.docxstamper.test.utils.IOStreams; - -import java.io.IOException; -import java.nio.file.Path; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.wickedsource.docxstamper.test.DefaultTests.getResource; - -/** - * @author Joseph Verron - * @author Tom Hombergs - */ -@DisplayName("Utilities - Docx Run Methods") -class RunUtilTest { - - @Test - void getTextReturnsTextOfRun() throws Docx4JException { - var document = loadDocument(Path.of("util","singleRun.docx")); - var paragraph = (P) document.getMainDocumentPart().getContent().get(0); - var run = (R) paragraph.getContent().get(0); - assertEquals("This is the only run of text in this document.", RunUtil.getText(run)); - } - - /** - * Loads a document from the given path. - * - * @param path the path to the document. - * @return the loaded document. - * @throws org.docx4j.openpackaging.exceptions.Docx4JException if any. - * @since 1.6.6 - */ - public WordprocessingMLPackage loadDocument(Path path) throws Docx4JException { - var in = getResource(path); - return WordprocessingMLPackage.load(in); - } - - @Test - void getTextReturnsValueDefinedBySetText() throws Docx4JException, IOException { - var input = loadDocument(Path.of("util","singleRun.docx")); - var paragraphIn = (P) input.getMainDocumentPart().getContent().get(0); - var runIn = (R) paragraphIn.getContent().get(0); - RunUtil.setText(runIn, "The text of this run was changed."); - var out = IOStreams.getOutputStream(); - input.save(out); - var in = IOStreams.getInputStream(out); - var output = WordprocessingMLPackage.load(in); - var paragraphOut = (P) output.getMainDocumentPart().getContent().get(0); - var runOut = (R) paragraphOut.getContent().get(0); - assertEquals("The text of this run was changed.", RunUtil.getText(runOut)); - } - -} diff --git a/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java b/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java index 99e56695..b1665698 100644 --- a/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java +++ b/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java @@ -1,10 +1,10 @@ package pro.verron.docxstamper.test.commentProcessors; +import org.docx4j.jaxb.Context; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.P; import org.docx4j.wml.R; import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; -import org.wickedsource.docxstamper.util.RunUtil; import pro.verron.docxstamper.api.AbstractCommentProcessor; import pro.verron.docxstamper.api.CommentWrapper; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; @@ -45,6 +45,19 @@ public CustomCommentProcessor(ParagraphPlaceholderReplacer placeholderReplacer) super(placeholderReplacer); } + public static R create(String string) { + var factory = Context.getWmlObjectFactory(); + + var text = factory.createText(); + text.setValue(string); + + var run = factory.createR(); + var runContent = run.getContent(); + runContent.add(text); + + return run; + } + /** * {@inheritDoc} */ @@ -53,7 +66,7 @@ public void commitChanges(WordprocessingMLPackage document) { visitedParagraphs.forEach(p -> { var content = p.getContent(); content.clear(); - content.add(RunUtil.create("Visited")); + content.add(create("Visited")); }); } From e58f9fe4daca4fcb271170abe1926ebb87e1c8dd Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Fri, 15 Mar 2024 06:10:29 +0100 Subject: [PATCH 032/134] Implement new CommentProcessor interface in pro.verron.docxstamper.api package This commit introduces the CommentProcessor interface within the pro.verron.docxstamper.api package. The previous org.wickedsource.docxstamper.api.commentprocessor package's visibility has been removed, while the new interface is exposed, making it more accessible within the module. With this change, all usage of the ICommentProcessor interface has been updated accordingly to use the new CommentProcessor interface. --- src/main/java/module-info.java | 2 +- .../commentprocessor/ICommentProcessor.java | 66 +------------------ .../docxstamper/api/CommentProcessor.java | 65 ++++++++++++++++++ .../CustomCommentProcessor.java | 4 +- .../ICustomCommentProcessor.java | 5 +- 5 files changed, 73 insertions(+), 69 deletions(-) create mode 100644 src/main/java/pro/verron/docxstamper/api/CommentProcessor.java diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 8002339b..15e99284 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -16,7 +16,7 @@ exports org.wickedsource.docxstamper; exports org.wickedsource.docxstamper.api; - exports org.wickedsource.docxstamper.api.commentprocessor; + // exports org.wickedsource.docxstamper.api.commentprocessor; // exports org.wickedsource.docxstamper.el; // exports org.wickedsource.docxstamper.util; // exports org.wickedsource.docxstamper.processor; diff --git a/src/main/java/org/wickedsource/docxstamper/api/commentprocessor/ICommentProcessor.java b/src/main/java/org/wickedsource/docxstamper/api/commentprocessor/ICommentProcessor.java index cdda0bc7..9278ca27 100644 --- a/src/main/java/org/wickedsource/docxstamper/api/commentprocessor/ICommentProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/api/commentprocessor/ICommentProcessor.java @@ -1,10 +1,5 @@ package org.wickedsource.docxstamper.api.commentprocessor; -import org.docx4j.openpackaging.packages.WordprocessingMLPackage; -import org.docx4j.wml.P; -import org.docx4j.wml.R; -import pro.verron.docxstamper.api.CommentWrapper; - /** *

In a .docx template used by DocxStamper, you can comment paragraphs of text to manipulate them. The comments in * the .docx template are passed to an implementation of ICommentProcessor that understands the expression used @@ -35,63 +30,6 @@ * @version ${version} * @since 1.0.0 */ -public interface ICommentProcessor { - - /** - * This method is called after all comments in the .docx template have been passed to the comment processor. - * All manipulations of the .docx document SHOULD BE done in this method. If certain manipulations are already done - * within the custom methods of a comment processor, the ongoing iteration over the paragraphs in the document - * may be disturbed. - * - * @param document The Word document that can be manipulated by using the DOCX4J api. - */ - void commitChanges(WordprocessingMLPackage document); - - /** - * Passes the paragraph that is currently being processed (i.e., the paragraph that is commented in the - * .docx template). This method is always called BEFORE the custom - * methods of the custom comment processor interface - * are called. - * - * @param paragraph coordinates of the currently processed paragraph within the template. - */ - void setParagraph(P paragraph); - - /** - * Passes the run that is currently being processed (i.e. the run that is commented in the - * .docx template). This method is always called BEFORE the custom - * methods of the custom comment processor interface - * are called. - * - * @param run coordinates of the currently processed run within the template. - */ - void setCurrentRun(R run); - - /** - * Passes the comment range wrapper that is currently being processed - * (i.e. the start and end of comment that in the .docx template). - * This method is always called BEFORE the custom methods of the custom comment - * processor interface are called. - * - * @param commentWrapper of the currently processed comment within the template. - */ - void setCurrentCommentWrapper(CommentWrapper commentWrapper); - - /** - * Passes the processed document, in order to make all linked data - * (images, etc.) available - * to processors that need it (example: repeatDocPart) - * - * @param document DocX template being processed. - * @deprecated the document is passed to the processor through the commitChange method now, - * and will probably pe passed through the constructor in the future - */ - - @Deprecated(since = "1.6.5", forRemoval = true) - void setDocument(WordprocessingMLPackage document); - - /** - * Resets all states in the comment processor so that it can be re-used in another stamping process. - */ - void reset(); +public interface ICommentProcessor + extends pro.verron.docxstamper.api.CommentProcessor { } diff --git a/src/main/java/pro/verron/docxstamper/api/CommentProcessor.java b/src/main/java/pro/verron/docxstamper/api/CommentProcessor.java new file mode 100644 index 00000000..06d0e143 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/api/CommentProcessor.java @@ -0,0 +1,65 @@ +package pro.verron.docxstamper.api; + +import org.docx4j.openpackaging.packages.WordprocessingMLPackage; +import org.docx4j.wml.P; +import org.docx4j.wml.R; + +public interface CommentProcessor { + /** + * This method is called after all comments in the .docx template have been passed to the comment processor. + * All manipulations of the .docx document SHOULD BE done in this method. If certain manipulations are already done + * within the custom methods of a comment processor, the ongoing iteration over the paragraphs in the document + * may be disturbed. + * + * @param document The Word document that can be manipulated by using the DOCX4J api. + */ + void commitChanges(WordprocessingMLPackage document); + + /** + * Passes the paragraph that is currently being processed (i.e., the paragraph that is commented in the + * .docx template). This method is always called BEFORE the custom + * methods of the custom comment processor interface + * are called. + * + * @param paragraph coordinates of the currently processed paragraph within the template. + */ + void setParagraph(P paragraph); + + /** + * Passes the run that is currently being processed (i.e. the run that is commented in the + * .docx template). This method is always called BEFORE the custom + * methods of the custom comment processor interface + * are called. + * + * @param run coordinates of the currently processed run within the template. + */ + void setCurrentRun(R run); + + /** + * Passes the comment range wrapper that is currently being processed + * (i.e. the start and end of comment that in the .docx template). + * This method is always called BEFORE the custom methods of the custom comment + * processor interface are called. + * + * @param commentWrapper of the currently processed comment within the template. + */ + void setCurrentCommentWrapper(CommentWrapper commentWrapper); + + /** + * Passes the processed document, in order to make all linked data + * (images, etc.) available + * to processors that need it (example: repeatDocPart) + * + * @param document DocX template being processed. + * @deprecated the document is passed to the processor through the commitChange method now, + * and will probably pe passed through the constructor in the future + */ + + @Deprecated(since = "1.6.5", forRemoval = true) + void setDocument(WordprocessingMLPackage document); + + /** + * Resets all states in the comment processor so that it can be re-used in another stamping process. + */ + void reset(); +} diff --git a/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java b/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java index b1665698..97c6f376 100644 --- a/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java +++ b/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java @@ -4,8 +4,8 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.P; import org.docx4j.wml.R; -import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import pro.verron.docxstamper.api.AbstractCommentProcessor; +import pro.verron.docxstamper.api.CommentProcessor; import pro.verron.docxstamper.api.CommentWrapper; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; @@ -13,7 +13,7 @@ import java.util.List; /** - * This is an example of a custom {@link ICommentProcessor} implementation. + * This is an example of a custom {@link CommentProcessor} implementation. *

* Users of the docx-stamper library could use it to understand how they could * leverage the library to create their own custom comment processors. diff --git a/src/test/java/pro/verron/docxstamper/test/commentProcessors/ICustomCommentProcessor.java b/src/test/java/pro/verron/docxstamper/test/commentProcessors/ICustomCommentProcessor.java index 0232bcaa..262031e8 100644 --- a/src/test/java/pro/verron/docxstamper/test/commentProcessors/ICustomCommentProcessor.java +++ b/src/test/java/pro/verron/docxstamper/test/commentProcessors/ICustomCommentProcessor.java @@ -1,6 +1,6 @@ package pro.verron.docxstamper.test.commentProcessors; -import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; +import pro.verron.docxstamper.api.CommentProcessor; /** *

ICustomCommentProcessor interface.

@@ -9,7 +9,8 @@ * @version ${version} * @since 1.6.6 */ -public interface ICustomCommentProcessor extends ICommentProcessor { +public interface ICustomCommentProcessor + extends CommentProcessor { /** *

visitParagraph.

*/ From 1bdd7508e2c24346cd0097b49ccc9a3a9564c3ae Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Fri, 15 Mar 2024 06:20:31 +0100 Subject: [PATCH 033/134] Update package references and adjust visibility settings The commit removes the export of `org.wickedsource.docxstamper.api` from the module-info, hiding its visibility. The references to classes in the `org.wickedsource.docxstamper.api` package are updated to their equivalents in `pro.verron.docxstamper.api`. Additionally, some code formatting and class structure are updated to adhere to coding standards. --- src/main/java/module-info.java | 2 +- .../org/wickedsource/docxstamper/DocxStamper.java | 2 +- .../docxstamper/DocxStamperConfiguration.java | 2 +- .../docxstamper/api/DocxStamperException.java | 3 ++- .../api/EvaluationContextConfigurer.java | 10 ++-------- .../docxstamper/api/DocxStamperException.java | 13 +++++++++++++ .../api/EvaluationContextConfigurer.java | 12 ++++++++++++ .../wickedsource/docxstamper/test/DefaultTests.java | 11 ++++++----- .../test/FailOnUnresolvedPlaceholderTest.java | 9 ++++++--- .../docxstamper/test/NullPointerResolutionTest.java | 2 +- .../test/RepeatDocPartBadPlaceholderTest.java | 2 +- .../verron/docxstamper/test/SpelInjectionTest.java | 2 +- .../verron/docxstamper/test/utils/Stringifier.java | 2 +- 13 files changed, 48 insertions(+), 24 deletions(-) create mode 100644 src/main/java/pro/verron/docxstamper/api/DocxStamperException.java create mode 100644 src/main/java/pro/verron/docxstamper/api/EvaluationContextConfigurer.java diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 15e99284..febedcc0 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -15,7 +15,7 @@ exports pro.verron.docxstamper.preset; exports org.wickedsource.docxstamper; - exports org.wickedsource.docxstamper.api; + // exports org.wickedsource.docxstamper.api; // exports org.wickedsource.docxstamper.api.commentprocessor; // exports org.wickedsource.docxstamper.el; // exports org.wickedsource.docxstamper.util; diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java index 6a26aaf0..ffd40324 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java @@ -7,13 +7,13 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.lang.NonNull; import org.wickedsource.docxstamper.api.DocxStamperException; -import org.wickedsource.docxstamper.api.EvaluationContextConfigurer; import org.wickedsource.docxstamper.api.preprocessor.PreProcessor; import org.wickedsource.docxstamper.el.ExpressionResolver; import org.wickedsource.docxstamper.el.StandardMethodResolver; import org.wickedsource.docxstamper.processor.CommentProcessorRegistry; import pro.verron.docxstamper.OpcStamper; import pro.verron.docxstamper.StamperFactory; +import pro.verron.docxstamper.api.EvaluationContextConfigurer; import pro.verron.docxstamper.api.ObjectResolver; import pro.verron.docxstamper.core.Expressions; import pro.verron.docxstamper.core.ObjectResolverRegistry; diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java index 76ade30a..763f2124 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java @@ -5,7 +5,6 @@ import org.springframework.expression.spel.SpelParserConfiguration; import org.springframework.lang.NonNull; import org.wickedsource.docxstamper.api.DocxStamperException; -import org.wickedsource.docxstamper.api.EvaluationContextConfigurer; import org.wickedsource.docxstamper.api.preprocessor.PreProcessor; import org.wickedsource.docxstamper.api.typeresolver.ITypeResolver; import org.wickedsource.docxstamper.el.DefaultEvaluationContextConfigurer; @@ -17,6 +16,7 @@ import org.wickedsource.docxstamper.processor.replaceExpression.IReplaceWithProcessor; import org.wickedsource.docxstamper.processor.table.ITableResolver; import org.wickedsource.docxstamper.replace.typeresolver.TypeResolver; +import pro.verron.docxstamper.api.EvaluationContextConfigurer; import pro.verron.docxstamper.api.ObjectResolver; import pro.verron.docxstamper.preset.Resolvers; import pro.verron.docxstamper.preset.resolver.Null2DefaultResolver; diff --git a/src/main/java/org/wickedsource/docxstamper/api/DocxStamperException.java b/src/main/java/org/wickedsource/docxstamper/api/DocxStamperException.java index e44fbdaf..996e6f90 100644 --- a/src/main/java/org/wickedsource/docxstamper/api/DocxStamperException.java +++ b/src/main/java/org/wickedsource/docxstamper/api/DocxStamperException.java @@ -9,7 +9,8 @@ * @version ${version} * @since 1.0.0 */ -public class DocxStamperException extends RuntimeException { +public class DocxStamperException + extends pro.verron.docxstamper.api.DocxStamperException { /** *

Constructor for DocxStamperException.

diff --git a/src/main/java/org/wickedsource/docxstamper/api/EvaluationContextConfigurer.java b/src/main/java/org/wickedsource/docxstamper/api/EvaluationContextConfigurer.java index cd30ee61..cc95b93e 100644 --- a/src/main/java/org/wickedsource/docxstamper/api/EvaluationContextConfigurer.java +++ b/src/main/java/org/wickedsource/docxstamper/api/EvaluationContextConfigurer.java @@ -3,7 +3,6 @@ import org.springframework.expression.EvaluationContext; import org.springframework.expression.MethodResolver; import org.springframework.expression.PropertyAccessor; -import org.springframework.expression.spel.support.StandardEvaluationContext; /** * Allows for custom configuration of a spring expression language {@link EvaluationContext}. @@ -14,11 +13,6 @@ * @version ${version} * @since 1.0.13 */ -public interface EvaluationContextConfigurer { - /** - * Configure the context before it's used by docxstamper. - * - * @param context the SPEL eval context, not null - */ - void configureEvaluationContext(StandardEvaluationContext context); +public interface EvaluationContextConfigurer + extends pro.verron.docxstamper.api.EvaluationContextConfigurer { } diff --git a/src/main/java/pro/verron/docxstamper/api/DocxStamperException.java b/src/main/java/pro/verron/docxstamper/api/DocxStamperException.java new file mode 100644 index 00000000..ff4b7589 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/api/DocxStamperException.java @@ -0,0 +1,13 @@ +package pro.verron.docxstamper.api; + +public class DocxStamperException + extends RuntimeException { + public DocxStamperException(String message) {super(message);} + + public DocxStamperException(Throwable cause) {super(cause);} + + public DocxStamperException(String message, Throwable cause) { + super(message, + cause); + } +} diff --git a/src/main/java/pro/verron/docxstamper/api/EvaluationContextConfigurer.java b/src/main/java/pro/verron/docxstamper/api/EvaluationContextConfigurer.java new file mode 100644 index 00000000..f6355f51 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/api/EvaluationContextConfigurer.java @@ -0,0 +1,12 @@ +package pro.verron.docxstamper.api; + +import org.springframework.expression.spel.support.StandardEvaluationContext; + +public interface EvaluationContextConfigurer { + /** + * Configure the context before it's used by docxstamper. + * + * @param context the SPEL eval context, not null + */ + void configureEvaluationContext(StandardEvaluationContext context); +} diff --git a/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java b/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java index 7dd0cb75..d06497e0 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java +++ b/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java @@ -6,7 +6,6 @@ import org.junit.jupiter.params.provider.MethodSource; import org.springframework.context.expression.MapAccessor; import org.springframework.expression.spel.SpelParserConfiguration; -import org.springframework.expression.spel.support.StandardEvaluationContext; import org.wickedsource.docxstamper.DocxStamperConfiguration; import pro.verron.docxstamper.api.Image; import pro.verron.docxstamper.preset.EvaluationContextConfigurers; @@ -252,8 +251,9 @@ private static Arguments repeatDocPartWithImageTestShouldImportImageDataInTheMai rId13:image/png:193.6kB:sha1=t8UNAmo7yJgZJk9g7pLLIb3AvCA=:cy=$d:6120130 """; - var config = new DocxStamperConfiguration().setEvaluationContextConfigurer( - ctx -> ctx.addPropertyAccessor(new MapAccessor())); + var config = new DocxStamperConfiguration() + .setEvaluationContextConfigurer( + ctx -> ctx.addPropertyAccessor(new MapAccessor())); return of( "repeatDocPartWithImageTestShouldImportImageDataInTheMainDocument", config, @@ -273,8 +273,9 @@ private static Image getImage(Path path) { private static Arguments repeatDocPartWithImagesInSourceTestshouldReplicateImageFromTheMainDocumentInTheSubTemplate() { return of( "repeatDocPartWithImagesInSourceTestshouldReplicateImageFromTheMainDocumentInTheSubTemplate", - new DocxStamperConfiguration().setEvaluationContextConfigurer((StandardEvaluationContext ctx) -> ctx.addPropertyAccessor( - new MapAccessor())), + new DocxStamperConfiguration() + .setEvaluationContextConfigurer( + (ctx) -> ctx.addPropertyAccessor(new MapAccessor())), Contexts.subDocPartContext(), getResource(Path.of("RepeatDocPartWithImagesInSourceTest" + ".docx")), diff --git a/src/test/java/org/wickedsource/docxstamper/test/FailOnUnresolvedPlaceholderTest.java b/src/test/java/org/wickedsource/docxstamper/test/FailOnUnresolvedPlaceholderTest.java index 6e9612f4..0af188e4 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/FailOnUnresolvedPlaceholderTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/FailOnUnresolvedPlaceholderTest.java @@ -4,7 +4,7 @@ import org.junit.jupiter.api.Test; import org.wickedsource.docxstamper.DocxStamper; import org.wickedsource.docxstamper.DocxStamperConfiguration; -import org.wickedsource.docxstamper.api.UnresolvedExpressionException; +import pro.verron.docxstamper.api.DocxStamperException; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -28,7 +28,8 @@ void fails() throws IOException { .setFailOnUnresolvedExpression(true); var stamper = new DocxStamper(config); var outputStream = new ByteArrayOutputStream(); - assertThrows(UnresolvedExpressionException.class, () -> stamper.stamp(template, context, outputStream)); + assertThrows(DocxStamperException.class, + () -> stamper.stamp(template, context, outputStream)); } } @@ -40,7 +41,9 @@ void doesNotFail() throws IOException { var config = new DocxStamperConfiguration() .setFailOnUnresolvedExpression(false); var stamper = new DocxStamper(config); - Assertions.assertDoesNotThrow(() -> stamper.stamp(template, context, new ByteArrayOutputStream())); + Assertions.assertDoesNotThrow(() -> stamper.stamp(template, + context, + new ByteArrayOutputStream())); } } diff --git a/src/test/java/org/wickedsource/docxstamper/test/NullPointerResolutionTest.java b/src/test/java/org/wickedsource/docxstamper/test/NullPointerResolutionTest.java index 25a7364e..04f76534 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/NullPointerResolutionTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/NullPointerResolutionTest.java @@ -2,7 +2,7 @@ import org.junit.jupiter.api.Test; import org.wickedsource.docxstamper.DocxStamperConfiguration; -import org.wickedsource.docxstamper.api.DocxStamperException; +import pro.verron.docxstamper.api.DocxStamperException; import pro.verron.docxstamper.test.utils.TestDocxStamper; import pro.verron.docxstamper.test.utils.context.NullishContext; import pro.verron.docxstamper.test.utils.context.SubContext; diff --git a/src/test/java/org/wickedsource/docxstamper/test/RepeatDocPartBadPlaceholderTest.java b/src/test/java/org/wickedsource/docxstamper/test/RepeatDocPartBadPlaceholderTest.java index f0c1cbb1..8c2cf8bd 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/RepeatDocPartBadPlaceholderTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/RepeatDocPartBadPlaceholderTest.java @@ -5,7 +5,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wickedsource.docxstamper.DocxStamperConfiguration; -import org.wickedsource.docxstamper.api.DocxStamperException; +import pro.verron.docxstamper.api.DocxStamperException; import pro.verron.docxstamper.test.utils.TestDocxStamper; import pro.verron.docxstamper.test.utils.context.Contexts.Characters; import pro.verron.docxstamper.test.utils.context.Contexts.Role; diff --git a/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java b/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java index 98998808..c3ae2021 100644 --- a/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java +++ b/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java @@ -2,7 +2,7 @@ import org.junit.jupiter.api.Test; import org.wickedsource.docxstamper.DocxStamperConfiguration; -import org.wickedsource.docxstamper.api.DocxStamperException; +import pro.verron.docxstamper.api.DocxStamperException; import pro.verron.docxstamper.test.utils.TestDocxStamper; import pro.verron.docxstamper.test.utils.context.Contexts; diff --git a/src/test/java/pro/verron/docxstamper/test/utils/Stringifier.java b/src/test/java/pro/verron/docxstamper/test/utils/Stringifier.java index 0f60d509..4ad9a146 100644 --- a/src/test/java/pro/verron/docxstamper/test/utils/Stringifier.java +++ b/src/test/java/pro/verron/docxstamper/test/utils/Stringifier.java @@ -15,7 +15,7 @@ import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage; import org.docx4j.openpackaging.parts.WordprocessingML.CommentsPart; import org.docx4j.wml.*; -import org.wickedsource.docxstamper.api.DocxStamperException; +import pro.verron.docxstamper.api.DocxStamperException; import java.math.BigInteger; import java.security.MessageDigest; From 5c30cf90bf4c5dbdb6bc50e97d5d4a74307dcd76 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Fri, 15 Mar 2024 07:28:15 +0100 Subject: [PATCH 034/134] Refactor project structure and move classes for better organization The changes made to the project mainly involve restructuring and organizing the project for better modularity and organization. Multiple classes have been moved to new packages and some have been renamed to better reflect their functionality. This will improve the maintainability and understanding of the codebase. The enhancement also involves moving the 'DocxStamperConfiguration' and 'DocxStamper' classes to a new package and made them less visible to allow for bigger projects to develop on top of them. --- src/main/java/module-info.java | 2 +- .../docxstamper/CommentProcessorBuilder.java | 21 - .../wickedsource/docxstamper/DocxStamper.java | 376 +++++++++--------- .../docxstamper/DocxStamperConfiguration.java | 47 ++- .../repeat/RepeatDocPartProcessor.java | 2 +- .../api/DocxStamperConfiguration.java | 113 ++++++ .../docxstamper/api/LoadingOpcStamper.java | 47 +++ .../docxstamper/{ => api}/OpcStamper.java | 3 +- .../preset}/CommentProcessorFactory.java | 4 +- .../docxstamper/preset/Configurations.java | 9 + .../Stampers.java} | 9 +- src/test/java/module-info.java | 1 - .../docxstamper/test/DefaultTests.java | 133 ++++--- .../test/FailOnUnresolvedPlaceholderTest.java | 22 +- .../docxstamper/test/MultiSectionTest.java | 9 +- .../docxstamper/test/MultiStampTest.java | 4 +- .../test/NullPointerResolutionTest.java | 6 +- ...olderReplacementInHeaderAndFooterTest.java | 7 +- ...PlaceholderReplacementInTextBoxesTest.java | 6 +- .../test/RepeatDocPartBadPlaceholderTest.java | 5 +- .../CustomCommentProcessor.java | 2 +- .../ICustomCommentProcessor.java | 2 +- .../docxstamper/test/SpelInjectionTest.java | 5 +- .../docxstamper/test/StampTableTest.java | 5 +- .../test/utils/TestDocxStamper.java | 19 +- 25 files changed, 542 insertions(+), 317 deletions(-) delete mode 100644 src/main/java/org/wickedsource/docxstamper/CommentProcessorBuilder.java create mode 100644 src/main/java/pro/verron/docxstamper/api/DocxStamperConfiguration.java create mode 100644 src/main/java/pro/verron/docxstamper/api/LoadingOpcStamper.java rename src/main/java/pro/verron/docxstamper/{ => api}/OpcStamper.java (89%) rename src/main/java/{org/wickedsource/docxstamper/processor => pro/verron/docxstamper/preset}/CommentProcessorFactory.java (97%) create mode 100644 src/main/java/pro/verron/docxstamper/preset/Configurations.java rename src/main/java/pro/verron/docxstamper/{StamperFactory.java => preset/Stampers.java} (84%) rename src/test/java/pro/verron/docxstamper/test/{commentProcessors => }/CustomCommentProcessor.java (98%) rename src/test/java/pro/verron/docxstamper/test/{commentProcessors => }/ICustomCommentProcessor.java (85%) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index febedcc0..e4b86c5d 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -14,7 +14,7 @@ exports pro.verron.docxstamper.api; exports pro.verron.docxstamper.preset; - exports org.wickedsource.docxstamper; + // exports org.wickedsource.docxstamper; // exports org.wickedsource.docxstamper.api; // exports org.wickedsource.docxstamper.api.commentprocessor; // exports org.wickedsource.docxstamper.el; diff --git a/src/main/java/org/wickedsource/docxstamper/CommentProcessorBuilder.java b/src/main/java/org/wickedsource/docxstamper/CommentProcessorBuilder.java deleted file mode 100644 index 89359be4..00000000 --- a/src/main/java/org/wickedsource/docxstamper/CommentProcessorBuilder.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.wickedsource.docxstamper; - -import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; -import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; - -/** - * Factory interface for creating {@link ICommentProcessor} instances. - * - * @author Joseph Verron - * @version ${version} - * @since 1.6.5 - */ -public interface CommentProcessorBuilder { - /** - * Creates a {@link ICommentProcessor} instance. - * - * @param placeholderReplacer the placeholder replacer that should be used by the comment processor. - * @return a {@link ICommentProcessor} instance. - */ - Object create(ParagraphPlaceholderReplacer placeholderReplacer); -} diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java index ffd40324..ffe259b5 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java @@ -11,13 +11,11 @@ import org.wickedsource.docxstamper.el.ExpressionResolver; import org.wickedsource.docxstamper.el.StandardMethodResolver; import org.wickedsource.docxstamper.processor.CommentProcessorRegistry; -import pro.verron.docxstamper.OpcStamper; -import pro.verron.docxstamper.StamperFactory; -import pro.verron.docxstamper.api.EvaluationContextConfigurer; -import pro.verron.docxstamper.api.ObjectResolver; +import pro.verron.docxstamper.api.*; import pro.verron.docxstamper.core.Expressions; import pro.verron.docxstamper.core.ObjectResolverRegistry; import pro.verron.docxstamper.core.PlaceholderReplacer; +import pro.verron.docxstamper.preset.Stampers; import java.io.InputStream; import java.io.OutputStream; @@ -35,179 +33,199 @@ * @version ${version} * @since 1.0.0 */ -public class DocxStamper implements OpcStamper { - private final List preprocessors; - private final PlaceholderReplacer placeholderReplacer; - private final CommentProcessorRegistry commentProcessorRegistry; - - /** - * Creates a new DocxStamper with the default configuration. - * - * @deprecated since 1.6.4, use {@link StamperFactory#newDocxStamper()} or {@link StamperFactory#nopreprocessingDocxStamper()} instead. - */ - @Deprecated(since = "1.6.4", forRemoval = true) - public DocxStamper() { - this(new DocxStamperConfiguration()); - } - - /** - * Creates a new DocxStamper with the given configuration. - * - * @param configuration the configuration to use for this DocxStamper. - */ - public DocxStamper(DocxStamperConfiguration configuration) { - this( - configuration.isFailOnUnresolvedExpression(), - configuration.isReplaceUnresolvedExpressions(), - configuration.isLeaveEmptyOnExpressionError(), - configuration.getUnresolvedExpressionsDefaultValue(), - configuration.getLineBreakPlaceholder(), - configuration.getEvaluationContextConfigurer(), - configuration.getExpressionFunctions(), - configuration.getResolvers(), - configuration.getCommentProcessors(), - configuration.getPreprocessors(), - configuration.getSpelParserConfiguration() - ); - } - - private DocxStamper( - boolean failOnUnresolvedExpression, - boolean replaceUnresolvedExpressions, - boolean leaveEmptyOnExpressionError, - String unresolvedExpressionsDefaultValue, - @NonNull String lineBreakPlaceholder, - EvaluationContextConfigurer evaluationContextConfigurer, - Map, Object> expressionFunctions, - List resolvers, - Map, CommentProcessorBuilder> configurationCommentProcessors, - List preprocessors, - SpelParserConfiguration spelParserConfiguration - ) { - var commentProcessors = new HashMap, Object>(); - - Function onResolutionFail = failOnUnresolvedExpression - ? DocxStamper::throwException - : exception -> new TypedValue(null); - - final StandardMethodResolver methodResolver = new StandardMethodResolver( - commentProcessors, - expressionFunctions, - onResolutionFail); - - var evaluationContext = new StandardEvaluationContext(); - evaluationContextConfigurer.configureEvaluationContext(evaluationContext); - evaluationContext.addMethodResolver(methodResolver); - - - var expressionResolver = new ExpressionResolver( - evaluationContext, - spelParserConfiguration - ); - - var typeResolverRegistry = new ObjectResolverRegistry(resolvers); - - var placeholderReplacerInstance = new PlaceholderReplacer( - typeResolverRegistry, - expressionResolver, - failOnUnresolvedExpression, - replaceUnresolvedExpressions, - unresolvedExpressionsDefaultValue, - leaveEmptyOnExpressionError, - Expressions.raw(lineBreakPlaceholder) - ); - - for (var entry : configurationCommentProcessors.entrySet()) { - commentProcessors.put(entry.getKey(), entry.getValue().create(placeholderReplacerInstance)); - } - - - var commentProcessorRegistryInstance = new CommentProcessorRegistry( - expressionResolver, - commentProcessors, - failOnUnresolvedExpression - ); - - this.placeholderReplacer = placeholderReplacerInstance; - this.commentProcessorRegistry = commentProcessorRegistryInstance; - this.preprocessors = preprocessors.stream().toList(); - } - - private static TypedValue throwException(ReflectiveOperationException exception) { - throw new DocxStamperException("Error calling method", exception); - } - - /** - * {@inheritDoc} - * - *

- * Reads in a .docx template and "stamps" it into the given OutputStream, using the specified context object to - * fill out any expressions it finds. - *

- *

- * In the .docx template you have the following options to influence the "stamping" process: - *

- *
    - *
  • Use expressions like ${name} or ${person.isOlderThan(18)} in the template's text. These expressions are resolved - * against the contextRoot object you pass into this method and are replaced by the results.
  • - *
  • Use comments within the .docx template to mark certain paragraphs to be manipulated.
  • - *
- *

- * Within comments, you can put expressions in which you can use the following methods by default: - *

- *
    - *
  • displayParagraphIf(boolean) to conditionally display paragraphs or not
  • - *
  • displayTableRowIf(boolean) to conditionally display table rows or not
  • - *
  • displayTableIf(boolean) to conditionally display whole tables or not
  • - *
  • repeatTableRow(List<Object>) to create a new table row for each object in the list and resolve expressions - * within the table cells against one of the objects within the list.
  • - *
- *

- * If you need a wider vocabulary of methods available in the comments, you can create your own ICommentProcessor - * and register it via {@link DocxStamperConfiguration#addCommentProcessor(Class, CommentProcessorBuilder)}. - *

- */ - public void stamp(InputStream template, Object contextRoot, OutputStream out) throws DocxStamperException { - try { - WordprocessingMLPackage document = WordprocessingMLPackage.load(template); - stamp(document, contextRoot, out); - } catch (Docx4JException e) { - throw new DocxStamperException(e); - } - } - - - /** - * {@inheritDoc} - * - * Same as {@link #stamp(InputStream, Object, OutputStream)} except that you - * may pass in a DOCX4J document as a template instead - * of an InputStream. - */ - @Override - public void stamp(WordprocessingMLPackage document, Object contextRoot, OutputStream out) throws DocxStamperException { - try { - preprocess(document); - processComments(document, contextRoot); - replaceExpressions(document, contextRoot); - document.save(out); - commentProcessorRegistry.reset(); - } catch (Docx4JException e) { - throw new DocxStamperException(e); - } - } - - private void preprocess(WordprocessingMLPackage document) { - for (PreProcessor preprocessor : preprocessors) { - preprocessor.process(document); - } - } - - private void processComments(final WordprocessingMLPackage document, Object contextObject) { - commentProcessorRegistry.runProcessors(document, contextObject); - } - - private void replaceExpressions(WordprocessingMLPackage document, Object contextObject) { - placeholderReplacer.resolveExpressions(document, contextObject); - } +public class DocxStamper + implements OpcStamper { + private final List preprocessors; + private final PlaceholderReplacer placeholderReplacer; + private final CommentProcessorRegistry commentProcessorRegistry; + + /** + * Creates a new DocxStamper with the default configuration. + * + * @deprecated since 1.6.4, use {@link Stampers#newDocxStamper()} or {@link Stampers#nopreprocessingDocxStamper()} instead. + */ + @Deprecated(since = "1.6.4", forRemoval = true) + public DocxStamper() { + this(new DocxStamperConfiguration()); + } + + /** + * Creates a new DocxStamper with the given configuration. + * + * @param configuration the configuration to use for this DocxStamper. + */ + public DocxStamper(pro.verron.docxstamper.api.DocxStamperConfiguration configuration) { + this( + configuration.isFailOnUnresolvedExpression(), + configuration.isReplaceUnresolvedExpressions(), + configuration.isLeaveEmptyOnExpressionError(), + configuration.getUnresolvedExpressionsDefaultValue(), + configuration.getLineBreakPlaceholder(), + configuration.getEvaluationContextConfigurer(), + configuration.getExpressionFunctions(), + configuration.getResolvers(), + configuration.getCommentProcessors(), + configuration.getPreprocessors(), + configuration.getSpelParserConfiguration() + ); + } + + private DocxStamper( + boolean failOnUnresolvedExpression, + boolean replaceUnresolvedExpressions, + boolean leaveEmptyOnExpressionError, + String unresolvedExpressionsDefaultValue, + @NonNull String lineBreakPlaceholder, + EvaluationContextConfigurer evaluationContextConfigurer, + Map, Object> expressionFunctions, + List resolvers, + Map, Function> configurationCommentProcessors, + List preprocessors, + SpelParserConfiguration spelParserConfiguration + ) { + var commentProcessors = new HashMap, Object>(); + + Function onResolutionFail = failOnUnresolvedExpression + ? DocxStamper::throwException + : exception -> new TypedValue(null); + + final StandardMethodResolver methodResolver = new StandardMethodResolver( + commentProcessors, + expressionFunctions, + onResolutionFail); + + var evaluationContext = new StandardEvaluationContext(); + evaluationContextConfigurer.configureEvaluationContext(evaluationContext); + evaluationContext.addMethodResolver(methodResolver); + + + var expressionResolver = new ExpressionResolver( + evaluationContext, + spelParserConfiguration + ); + + var typeResolverRegistry = new ObjectResolverRegistry(resolvers); + + var placeholderReplacer = new PlaceholderReplacer( + typeResolverRegistry, + expressionResolver, + failOnUnresolvedExpression, + replaceUnresolvedExpressions, + unresolvedExpressionsDefaultValue, + leaveEmptyOnExpressionError, + Expressions.raw(lineBreakPlaceholder) + ); + + for (var entry : configurationCommentProcessors.entrySet()) { + Class aClass = entry.getKey(); + Function processorFunction = entry.getValue(); + CommentProcessor value = processorFunction.apply(placeholderReplacer); + commentProcessors.put(aClass, value); + } + + + var commentProcessorRegistryInstance = new CommentProcessorRegistry( + expressionResolver, + commentProcessors, + failOnUnresolvedExpression + ); + + this.placeholderReplacer = placeholderReplacer; + this.commentProcessorRegistry = commentProcessorRegistryInstance; + this.preprocessors = preprocessors.stream() + .toList(); + } + + private static TypedValue throwException(ReflectiveOperationException exception) { + throw new DocxStamperException("Error calling method", exception); + } + + /** + * {@inheritDoc} + * + *

+ * Reads in a .docx template and "stamps" it into the given OutputStream, using the specified context object to + * fill out any expressions it finds. + *

+ *

+ * In the .docx template you have the following options to influence the "stamping" process: + *

+ *
    + *
  • Use expressions like ${name} or ${person.isOlderThan(18)} in the template's text. These expressions are resolved + * against the contextRoot object you pass into this method and are replaced by the results.
  • + *
  • Use comments within the .docx template to mark certain paragraphs to be manipulated.
  • + *
+ *

+ * Within comments, you can put expressions in which you can use the following methods by default: + *

+ *
    + *
  • displayParagraphIf(boolean) to conditionally display paragraphs or not
  • + *
  • displayTableRowIf(boolean) to conditionally display table rows or not
  • + *
  • displayTableIf(boolean) to conditionally display whole tables or not
  • + *
  • repeatTableRow(List<Object>) to create a new table row for each object in the list and resolve expressions + * within the table cells against one of the objects within the list.
  • + *
+ *

+ * If you need a wider vocabulary of methods available in the comments, you can create your own ICommentProcessor + * and register it via {@link DocxStamperConfiguration#addCommentProcessor(Class, CommentProcessorBuilder)}. + *

+ */ + public void stamp( + InputStream template, + Object contextRoot, + OutputStream out + ) throws DocxStamperException { + try { + WordprocessingMLPackage document = WordprocessingMLPackage.load( + template); + stamp(document, contextRoot, out); + } catch (Docx4JException e) { + throw new DocxStamperException(e); + } + } + + + /** + * {@inheritDoc} + *

+ * Same as {@link #stamp(InputStream, Object, OutputStream)} except that you + * may pass in a DOCX4J document as a template instead + * of an InputStream. + */ + @Override + public void stamp( + WordprocessingMLPackage document, + Object contextRoot, + OutputStream out + ) throws DocxStamperException { + try { + preprocess(document); + processComments(document, contextRoot); + replaceExpressions(document, contextRoot); + document.save(out); + commentProcessorRegistry.reset(); + } catch (Docx4JException e) { + throw new DocxStamperException(e); + } + } + + private void preprocess(WordprocessingMLPackage document) { + for (PreProcessor preprocessor : preprocessors) { + preprocessor.process(document); + } + } + + private void processComments( + final WordprocessingMLPackage document, + Object contextObject + ) { + commentProcessorRegistry.runProcessors(document, contextObject); + } + + private void replaceExpressions( + WordprocessingMLPackage document, + Object contextObject + ) { + placeholderReplacer.resolveExpressions(document, contextObject); + } } diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java index 763f2124..929cb1eb 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java @@ -8,7 +8,6 @@ import org.wickedsource.docxstamper.api.preprocessor.PreProcessor; import org.wickedsource.docxstamper.api.typeresolver.ITypeResolver; import org.wickedsource.docxstamper.el.DefaultEvaluationContextConfigurer; -import org.wickedsource.docxstamper.processor.CommentProcessorFactory; import org.wickedsource.docxstamper.processor.displayif.IDisplayIfProcessor; import org.wickedsource.docxstamper.processor.repeat.IParagraphRepeatProcessor; import org.wickedsource.docxstamper.processor.repeat.IRepeatDocPartProcessor; @@ -16,12 +15,16 @@ import org.wickedsource.docxstamper.processor.replaceExpression.IReplaceWithProcessor; import org.wickedsource.docxstamper.processor.table.ITableResolver; import org.wickedsource.docxstamper.replace.typeresolver.TypeResolver; +import pro.verron.docxstamper.api.CommentProcessor; import pro.verron.docxstamper.api.EvaluationContextConfigurer; import pro.verron.docxstamper.api.ObjectResolver; +import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; +import pro.verron.docxstamper.preset.CommentProcessorFactory; import pro.verron.docxstamper.preset.Resolvers; import pro.verron.docxstamper.preset.resolver.Null2DefaultResolver; import java.util.*; +import java.util.function.Function; import static java.util.stream.Collectors.toMap; @@ -34,9 +37,10 @@ * @version ${version} * @since 1.0.3 */ -public class DocxStamperConfiguration { +public class DocxStamperConfiguration + implements pro.verron.docxstamper.api.DocxStamperConfiguration { - private final Map, CommentProcessorBuilder> commentProcessors = new HashMap<>(); + private final Map, Function> commentProcessors = new HashMap<>(); private final List resolvers = new ArrayList<>(); private final Map, Object> expressionFunctions = new HashMap<>(); private final List preprocessors = new ArrayList<>(); @@ -83,6 +87,7 @@ public DocxStamperConfiguration() { * You shouldn't have to use it, it was a crutch use for inner working of * docx-stamper */ + @Override @Deprecated(since = "1.6.7") public Optional nullReplacementValue() { return resolvers.stream() @@ -97,6 +102,7 @@ public Optional nullReplacementValue() { * * @return a boolean */ + @Override public boolean isFailOnUnresolvedExpression() { return failOnUnresolvedExpression; } @@ -108,6 +114,7 @@ public boolean isFailOnUnresolvedExpression() { * @param failOnUnresolvedExpression a boolean * @return a {@link DocxStamperConfiguration} object */ + @Override public DocxStamperConfiguration setFailOnUnresolvedExpression(boolean failOnUnresolvedExpression) { this.failOnUnresolvedExpression = failOnUnresolvedExpression; return this; @@ -123,6 +130,7 @@ public DocxStamperConfiguration setFailOnUnresolvedExpression(boolean failOnUnre * {@link DocxStamperConfiguration#addResolver(ObjectResolver)} and * {@link Resolvers#nullToDefault(String)} instead. */ + @Override @Deprecated(since = "1.6.7", forRemoval = true) public DocxStamperConfiguration nullValuesDefault(String nullValuesDefault) { this.nullValuesDefault = nullValuesDefault; @@ -143,6 +151,7 @@ public DocxStamperConfiguration nullValuesDefault(String nullValuesDefault) { * {@link DocxStamperConfiguration#addResolver(ObjectResolver)} and * {@link Resolvers#nullToDefault(String)} instead. */ + @Override @Deprecated(since = "1.6.7", forRemoval = true) public DocxStamperConfiguration replaceNullValues(boolean replaceNullValues) { this.replaceNullValues = replaceNullValues; @@ -159,6 +168,7 @@ public DocxStamperConfiguration replaceNullValues(boolean replaceNullValues) { * @return a {@link DocxStamperConfiguration} object * @see DocxStamperConfiguration#replaceUnresolvedExpressions */ + @Override public DocxStamperConfiguration unresolvedExpressionsDefaultValue(String unresolvedExpressionsDefaultValue) { this.unresolvedExpressionsDefaultValue = unresolvedExpressionsDefaultValue; return this; @@ -170,6 +180,7 @@ public DocxStamperConfiguration unresolvedExpressionsDefaultValue(String unresol * @param replaceUnresolvedExpressions true to replace null value expression with resolved value (which is null), false to leave the expression as is * @return a {@link DocxStamperConfiguration} object */ + @Override public DocxStamperConfiguration replaceUnresolvedExpressions(boolean replaceUnresolvedExpressions) { this.replaceUnresolvedExpressions = replaceUnresolvedExpressions; return this; @@ -182,6 +193,7 @@ public DocxStamperConfiguration replaceUnresolvedExpressions(boolean replaceUnre * @param leaveEmpty true to replace expressions with empty string when an error is caught while evaluating * @return a {@link DocxStamperConfiguration} object */ + @Override public DocxStamperConfiguration leaveEmptyOnExpressionError(boolean leaveEmpty) { this.leaveEmptyOnExpressionError = leaveEmpty; return this; @@ -207,6 +219,7 @@ public DocxStamperConfiguration leaveEmptyOnExpressionError(boolean leaveEmpty) * It's recommended to use the {@link DocxStamperConfiguration#addResolver(ObjectResolver)} * method for adding resolvers. */ + @Override @Deprecated(since = "1.6.7", forRemoval = true) public DocxStamperConfiguration addTypeResolver( Class resolvedType, ITypeResolver resolver @@ -223,6 +236,7 @@ public DocxStamperConfiguration addTypeResolver( * within the expression language. Must implement the interface above. * @return a {@link DocxStamperConfiguration} object */ + @Override public DocxStamperConfiguration exposeInterfaceToExpressionLanguage( Class interfaceClass, Object implementation ) { @@ -238,9 +252,10 @@ public DocxStamperConfiguration exposeInterfaceToExpressionLanguage( * @param commentProcessorFactory the commentProcessor factory generating the specified interface. * @return a {@link DocxStamperConfiguration} object */ + @Override public DocxStamperConfiguration addCommentProcessor( Class interfaceClass, - CommentProcessorBuilder commentProcessorFactory + Function commentProcessorFactory ) { this.commentProcessors.put(interfaceClass, commentProcessorFactory); return this; @@ -253,6 +268,7 @@ public DocxStamperConfiguration addCommentProcessor( * @return a {@link DocxStamper} object * @deprecated use new {@link DocxStamper#DocxStamper(DocxStamperConfiguration)} instead */ + @Override @Deprecated(forRemoval = true, since = "1.6.4") public DocxStamper build() { return new DocxStamper<>(this); @@ -263,6 +279,7 @@ public DocxStamper build() { * * @param preprocessor the preprocessor to add. */ + @Override public void addPreprocessor(PreProcessor preprocessor) { preprocessors.add(preprocessor); } @@ -272,6 +289,7 @@ public void addPreprocessor(PreProcessor preprocessor) { * * @return a boolean */ + @Override public boolean isReplaceUnresolvedExpressions() { return replaceUnresolvedExpressions; } @@ -281,6 +299,7 @@ public boolean isReplaceUnresolvedExpressions() { * * @return a boolean */ + @Override public boolean isLeaveEmptyOnExpressionError() { return leaveEmptyOnExpressionError; } @@ -290,6 +309,7 @@ public boolean isLeaveEmptyOnExpressionError() { * * @return a {@link String} object */ + @Override public String getUnresolvedExpressionsDefaultValue() { return unresolvedExpressionsDefaultValue; } @@ -299,6 +319,7 @@ public String getUnresolvedExpressionsDefaultValue() { * * @return a {@link String} object */ + @Override public String getLineBreakPlaceholder() { return lineBreakPlaceholder; } @@ -311,6 +332,7 @@ public String getLineBreakPlaceholder() { * @param lineBreakPlaceholder the String that should be replaced with line breaks during stamping. * @return the configuration object for chaining. */ + @Override public DocxStamperConfiguration setLineBreakPlaceholder(@NonNull String lineBreakPlaceholder) { this.lineBreakPlaceholder = lineBreakPlaceholder; return this; @@ -321,6 +343,7 @@ public DocxStamperConfiguration setLineBreakPlaceholder(@NonNull String lineBrea * * @return a {@link EvaluationContextConfigurer} object */ + @Override public EvaluationContextConfigurer getEvaluationContextConfigurer() { return evaluationContextConfigurer; } @@ -333,6 +356,7 @@ public EvaluationContextConfigurer getEvaluationContextConfigurer() { * @param evaluationContextConfigurer the configurer to use. * @return a {@link DocxStamperConfiguration} object */ + @Override public DocxStamperConfiguration setEvaluationContextConfigurer( EvaluationContextConfigurer evaluationContextConfigurer ) { @@ -345,6 +369,7 @@ public DocxStamperConfiguration setEvaluationContextConfigurer( * * @return a {@link SpelParserConfiguration} object */ + @Override public SpelParserConfiguration getSpelParserConfiguration() { return spelParserConfiguration; } @@ -358,6 +383,7 @@ public SpelParserConfiguration getSpelParserConfiguration() { * @param spelParserConfiguration the configuration to use. * @return a {@link DocxStamperConfiguration} object */ + @Override public DocxStamperConfiguration setSpelParserConfiguration( SpelParserConfiguration spelParserConfiguration ) { @@ -370,6 +396,7 @@ public DocxStamperConfiguration setSpelParserConfiguration( * * @return a {@link Map} object */ + @Override public Map, Object> getExpressionFunctions() { return expressionFunctions; } @@ -380,6 +407,7 @@ public Map, Object> getExpressionFunctions() { * @return the map of type resolvers * @deprecated This method's been deprecated since version 1.6.7 */ + @Override @Deprecated(since = "1.6.7", forRemoval = true) public Map, ITypeResolver> getTypeResolvers() { return resolvers.stream() @@ -399,6 +427,7 @@ public Map, ITypeResolver> getTypeResolvers() { * You should not have to use it, it was a clutch for previous version of * docx-stamper. */ + @Override @Deprecated(since = "1.6.7", forRemoval = true) public ITypeResolver getDefaultTypeResolver() { return (ITypeResolver) resolvers.stream() @@ -420,6 +449,7 @@ public ITypeResolver getDefaultTypeResolver() { * {@link DocxStamperConfiguration#setResolvers(List)}} and putting your * fallback resolvers in the last place. */ + @Override @Deprecated(since = "1.6.7", forRemoval = true) public DocxStamperConfiguration setDefaultTypeResolver( ITypeResolver defaultResolver @@ -434,7 +464,8 @@ public DocxStamperConfiguration setDefaultTypeResolver( * * @return a {@link Map} object */ - public Map, CommentProcessorBuilder> getCommentProcessors() { + @Override + public Map, Function> getCommentProcessors() { return commentProcessors; } @@ -446,6 +477,7 @@ public Map, CommentProcessorBuilder> getCommentProcessors() { * You shouldn't have to use it, it was a clutch for * docx-stamper workings. */ + @Override @Deprecated(since = "1.6.7", forRemoval = true) public boolean isReplaceNullValues() { return replaceNullValues; @@ -458,6 +490,7 @@ public boolean isReplaceNullValues() { * @deprecated This method has been deprecated since version 1.6.7 and is scheduled for removal. * You shouldn't have to use it, it was a clutch for docx-stamper workings. */ + @Override @Deprecated(since = "1.6.7", forRemoval = true) public String getNullValuesDefault() { return nullValuesDefault; @@ -468,6 +501,7 @@ public String getNullValuesDefault() { * * @return a {@link List} object */ + @Override public List getPreprocessors() { return preprocessors; } @@ -477,6 +511,7 @@ public List getPreprocessors() { * * @return The list of object resolvers. */ + @Override public List getResolvers() { return resolvers; } @@ -491,6 +526,7 @@ public List getResolvers() { * @param resolvers The list of ObjectResolvers to be set. * @return The updated DocxStamperConfiguration instance. */ + @Override public DocxStamperConfiguration setResolvers( List resolvers ) { @@ -506,6 +542,7 @@ public DocxStamperConfiguration setResolvers( * @param resolver The resolver to be added. This resolver should implement the `ObjectResolver` interface. * @return The modified `DocxStamperConfiguration` object, with the resolver added to the beginning of the resolver list. */ + @Override public DocxStamperConfiguration addResolver(ObjectResolver resolver) { resolvers.add(0, resolver); return this; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java index 50a1e41d..2bedb2a2 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java @@ -11,8 +11,8 @@ import org.wickedsource.docxstamper.util.DocumentUtil; import org.wickedsource.docxstamper.util.ParagraphUtil; import org.wickedsource.docxstamper.util.SectionUtil; -import pro.verron.docxstamper.OpcStamper; import pro.verron.docxstamper.api.CommentWrapper; +import pro.verron.docxstamper.api.OpcStamper; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.core.PlaceholderReplacer; diff --git a/src/main/java/pro/verron/docxstamper/api/DocxStamperConfiguration.java b/src/main/java/pro/verron/docxstamper/api/DocxStamperConfiguration.java new file mode 100644 index 00000000..92014fdd --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/api/DocxStamperConfiguration.java @@ -0,0 +1,113 @@ +package pro.verron.docxstamper.api; + +import org.springframework.expression.spel.SpelParserConfiguration; +import org.springframework.lang.NonNull; +import org.wickedsource.docxstamper.DocxStamper; +import org.wickedsource.docxstamper.api.preprocessor.PreProcessor; +import org.wickedsource.docxstamper.api.typeresolver.ITypeResolver; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + +public interface DocxStamperConfiguration { + @Deprecated(since = "1.6.7") + Optional nullReplacementValue(); + + boolean isFailOnUnresolvedExpression(); + + DocxStamperConfiguration setFailOnUnresolvedExpression(boolean failOnUnresolvedExpression); + + @Deprecated(since = "1.6.7", forRemoval = true) + DocxStamperConfiguration nullValuesDefault(String nullValuesDefault); + + @Deprecated(since = "1.6.7", forRemoval = true) + DocxStamperConfiguration replaceNullValues(boolean replaceNullValues); + + DocxStamperConfiguration unresolvedExpressionsDefaultValue(String unresolvedExpressionsDefaultValue); + + DocxStamperConfiguration replaceUnresolvedExpressions( + boolean replaceUnresolvedExpressions + ); + + DocxStamperConfiguration leaveEmptyOnExpressionError( + boolean leaveEmpty + ); + + @Deprecated(since = "1.6.7", forRemoval = true) + DocxStamperConfiguration addTypeResolver( + Class resolvedType, ITypeResolver resolver + ); + + DocxStamperConfiguration exposeInterfaceToExpressionLanguage( + Class interfaceClass, Object implementation + ); + + DocxStamperConfiguration addCommentProcessor( + Class interfaceClass, + Function commentProcessorFactory + ); + + @Deprecated(forRemoval = true, since = "1.6.4") + DocxStamper build(); + + void addPreprocessor(PreProcessor preprocessor); + + boolean isReplaceUnresolvedExpressions(); + + boolean isLeaveEmptyOnExpressionError(); + + String getUnresolvedExpressionsDefaultValue(); + + String getLineBreakPlaceholder(); + + DocxStamperConfiguration setLineBreakPlaceholder( + @NonNull String lineBreakPlaceholder + ); + + EvaluationContextConfigurer getEvaluationContextConfigurer(); + + DocxStamperConfiguration setEvaluationContextConfigurer( + EvaluationContextConfigurer evaluationContextConfigurer + ); + + SpelParserConfiguration getSpelParserConfiguration(); + + DocxStamperConfiguration setSpelParserConfiguration( + SpelParserConfiguration spelParserConfiguration + ); + + Map, Object> getExpressionFunctions(); + + @Deprecated(since = "1.6.7", forRemoval = true) + Map, ITypeResolver> getTypeResolvers(); + + @Deprecated(since = "1.6.7", forRemoval = true) + ITypeResolver getDefaultTypeResolver(); + + @Deprecated(since = "1.6.7", forRemoval = true) + DocxStamperConfiguration setDefaultTypeResolver( + ITypeResolver defaultResolver + ); + + Map, Function> getCommentProcessors(); + + @Deprecated(since = "1.6.7", forRemoval = true) + boolean isReplaceNullValues(); + + @Deprecated(since = "1.6.7", forRemoval = true) + String getNullValuesDefault(); + + List getPreprocessors(); + + List getResolvers(); + + DocxStamperConfiguration setResolvers( + List resolvers + ); + + DocxStamperConfiguration addResolver( + ObjectResolver resolver + ); +} diff --git a/src/main/java/pro/verron/docxstamper/api/LoadingOpcStamper.java b/src/main/java/pro/verron/docxstamper/api/LoadingOpcStamper.java new file mode 100644 index 00000000..9b165407 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/api/LoadingOpcStamper.java @@ -0,0 +1,47 @@ +package pro.verron.docxstamper.api; + +import org.docx4j.openpackaging.packages.OpcPackage; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.function.Function; + +/** + * OpcStamper is an interface that defines the contract for stamping + * templates with context and writing the result to an {@link OutputStream}. + * + * @param The type of the template that can be stamped. + * @author Joseph Verron + * @version ${version} + * @since 1.6.4 + */ +public class LoadingOpcStamper { + + private final Function loader; + private final OpcStamper stamper; + + public LoadingOpcStamper( + Function loader, + OpcStamper stamper + ) { + this.loader = loader; + this.stamper = stamper; + } + + /** + * Stamps the template with the context and writes the result to the outputStream. + * + * @param inputStream template to stamp + * @param context context to use for stamping + * @param outputStream output stream to write the result to + * @throws DocxStamperException if the stamping fails + */ + public void stamp( + InputStream inputStream, + Object context, + OutputStream outputStream + ) throws DocxStamperException { + T mlPackage = loader.apply(inputStream); + stamper.stamp(mlPackage, context, outputStream); + } +} diff --git a/src/main/java/pro/verron/docxstamper/OpcStamper.java b/src/main/java/pro/verron/docxstamper/api/OpcStamper.java similarity index 89% rename from src/main/java/pro/verron/docxstamper/OpcStamper.java rename to src/main/java/pro/verron/docxstamper/api/OpcStamper.java index 501b9570..49499578 100644 --- a/src/main/java/pro/verron/docxstamper/OpcStamper.java +++ b/src/main/java/pro/verron/docxstamper/api/OpcStamper.java @@ -1,7 +1,6 @@ -package pro.verron.docxstamper; +package pro.verron.docxstamper.api; import org.docx4j.openpackaging.packages.OpcPackage; -import org.wickedsource.docxstamper.api.DocxStamperException; import java.io.OutputStream; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorFactory.java b/src/main/java/pro/verron/docxstamper/preset/CommentProcessorFactory.java similarity index 97% rename from src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorFactory.java rename to src/main/java/pro/verron/docxstamper/preset/CommentProcessorFactory.java index 9b89fe0e..c8fe1c3a 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorFactory.java +++ b/src/main/java/pro/verron/docxstamper/preset/CommentProcessorFactory.java @@ -1,4 +1,4 @@ -package org.wickedsource.docxstamper.processor; +package pro.verron.docxstamper.preset; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.wickedsource.docxstamper.DocxStamper; @@ -10,7 +10,7 @@ import org.wickedsource.docxstamper.processor.repeat.RepeatProcessor; import org.wickedsource.docxstamper.processor.replaceExpression.ReplaceWithProcessor; import org.wickedsource.docxstamper.processor.table.TableResolver; -import pro.verron.docxstamper.OpcStamper; +import pro.verron.docxstamper.api.OpcStamper; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.core.PlaceholderReplacer; diff --git a/src/main/java/pro/verron/docxstamper/preset/Configurations.java b/src/main/java/pro/verron/docxstamper/preset/Configurations.java new file mode 100644 index 00000000..9f1e8c4f --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/preset/Configurations.java @@ -0,0 +1,9 @@ +package pro.verron.docxstamper.preset; + +import pro.verron.docxstamper.api.DocxStamperConfiguration; + +public class Configurations { + public static DocxStamperConfiguration standard() { + return new org.wickedsource.docxstamper.DocxStamperConfiguration(); + } +} diff --git a/src/main/java/pro/verron/docxstamper/StamperFactory.java b/src/main/java/pro/verron/docxstamper/preset/Stampers.java similarity index 84% rename from src/main/java/pro/verron/docxstamper/StamperFactory.java rename to src/main/java/pro/verron/docxstamper/preset/Stampers.java index a822ec30..b9bf890f 100644 --- a/src/main/java/pro/verron/docxstamper/StamperFactory.java +++ b/src/main/java/pro/verron/docxstamper/preset/Stampers.java @@ -1,10 +1,11 @@ -package pro.verron.docxstamper; +package pro.verron.docxstamper.preset; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.wickedsource.docxstamper.DocxStamper; import org.wickedsource.docxstamper.DocxStamperConfiguration; import org.wickedsource.docxstamper.preprocessor.MergeSameStyleRuns; import org.wickedsource.docxstamper.preprocessor.RemoveProofErrors; +import pro.verron.docxstamper.api.OpcStamper; /** * Main class of the docx-stamper library. @@ -16,7 +17,11 @@ * @version ${version} * @since 1.6.4 */ -public class StamperFactory { +public class Stampers { + + public static OpcStamper from(pro.verron.docxstamper.api.DocxStamperConfiguration config) { + return new DocxStamper<>(config); + } /** * Creates a new DocxStamper with the default configuration. diff --git a/src/test/java/module-info.java b/src/test/java/module-info.java index f43dc1cb..b5fed305 100644 --- a/src/test/java/module-info.java +++ b/src/test/java/module-info.java @@ -15,7 +15,6 @@ requires jakarta.xml.bind; opens pro.verron.docxstamper.test; - opens pro.verron.docxstamper.test.commentProcessors; opens pro.verron.docxstamper.test.utils.context; opens org.wickedsource.docxstamper.test; diff --git a/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java b/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java index d06497e0..6ed5c4f1 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java +++ b/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java @@ -6,14 +6,15 @@ import org.junit.jupiter.params.provider.MethodSource; import org.springframework.context.expression.MapAccessor; import org.springframework.expression.spel.SpelParserConfiguration; -import org.wickedsource.docxstamper.DocxStamperConfiguration; +import pro.verron.docxstamper.api.DocxStamperConfiguration; import pro.verron.docxstamper.api.Image; +import pro.verron.docxstamper.preset.Configurations; import pro.verron.docxstamper.preset.EvaluationContextConfigurers; import pro.verron.docxstamper.preset.Resolvers; +import pro.verron.docxstamper.test.CustomCommentProcessor; import pro.verron.docxstamper.test.Functions; +import pro.verron.docxstamper.test.ICustomCommentProcessor; import pro.verron.docxstamper.test.accessors.SimpleGetter; -import pro.verron.docxstamper.test.commentProcessors.CustomCommentProcessor; -import pro.verron.docxstamper.test.commentProcessors.ICustomCommentProcessor; import pro.verron.docxstamper.test.resolver.CustomTypeResolver; import pro.verron.docxstamper.test.utils.TestDocxStamper; import pro.verron.docxstamper.test.utils.context.Contexts; @@ -60,7 +61,7 @@ public static InputStream getResource(Path path) { private static Arguments replaceWordWithIntegrationTest() { return of("replaceWordWithIntegrationTest", - new DocxStamperConfiguration(), + Configurations.standard(), name("Simpsons"), getResource(Path.of("integration", "ReplaceWordWithIntegrationTest.docx")), @@ -73,7 +74,7 @@ private static Arguments replaceWordWithIntegrationTest() { private static Arguments repeatingRows() { return of("Repeating table rows should be possible", - new DocxStamperConfiguration(), + Configurations.standard(), roles(role("Homer Simpson", "Dan Castellaneta"), role("Marge Simpson", "Julie Kavner"), role("Bart Simpson", "Nancy Cartwright"), @@ -104,7 +105,7 @@ private static Arguments repeatingRows() { private static Arguments ternary() { return of("Ternary operators should function", - new DocxStamperConfiguration(), + Configurations.standard(), name("Homer"), getResource(Path.of("TernaryOperatorTest.docx")), """ @@ -117,7 +118,7 @@ private static Arguments ternary() { private static Arguments whitespaces() { return of("White spaces should be preserved", - new DocxStamperConfiguration(), + Configurations.standard(), name("Homer Simpson"), getResource(Path.of("TabsIndentationTest.docx")), """ @@ -127,7 +128,7 @@ private static Arguments whitespaces() { private static Arguments tabulations() { return of("Tabulation should be preserved", - new DocxStamperConfiguration(), + Configurations.standard(), name("Homer Simpson"), getResource(Path.of("TabsIndentationTest.docx")), """ @@ -137,7 +138,7 @@ private static Arguments tabulations() { private static Arguments replaceNullExpressionTest() { return of("Do not replace 'null' values", - new DocxStamperConfiguration() + Configurations.standard() .addResolver(Resolvers.nullToPlaceholder()), name(null), getResource(Path.of("ReplaceNullExpressionTest.docx")), @@ -146,7 +147,7 @@ private static Arguments replaceNullExpressionTest() { private static Arguments replaceNullExpressionTest2() { return of("Do replace 'null' values", - new DocxStamperConfiguration() + Configurations.standard() .addResolver(Resolvers.nullToEmpty()), name(null), getResource(Path.of("ReplaceNullExpressionTest.docx")), @@ -155,7 +156,7 @@ private static Arguments replaceNullExpressionTest2() { private static Arguments repeatTableRowKeepsFormatTest() { return of("repeatTableRowKeepsFormatTest", - new DocxStamperConfiguration(), + Configurations.standard(), new Show(List.of(new CharacterRecord(1, "st", "Homer Simpson", @@ -226,7 +227,7 @@ private static Arguments repeatParagraphTest() { ❬There are ❬6❘lang=de-DE❭ characters.❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; return arguments("repeatParagraphTest", - new DocxStamperConfiguration(), + Configurations.standard(), context, template, expected); @@ -251,7 +252,7 @@ private static Arguments repeatDocPartWithImageTestShouldImportImageDataInTheMai rId13:image/png:193.6kB:sha1=t8UNAmo7yJgZJk9g7pLLIb3AvCA=:cy=$d:6120130 """; - var config = new DocxStamperConfiguration() + var config = Configurations.standard() .setEvaluationContextConfigurer( ctx -> ctx.addPropertyAccessor(new MapAccessor())); return of( @@ -273,7 +274,7 @@ private static Image getImage(Path path) { private static Arguments repeatDocPartWithImagesInSourceTestshouldReplicateImageFromTheMainDocumentInTheSubTemplate() { return of( "repeatDocPartWithImagesInSourceTestshouldReplicateImageFromTheMainDocumentInTheSubTemplate", - new DocxStamperConfiguration() + Configurations.standard() .setEvaluationContextConfigurer( (ctx) -> ctx.addPropertyAccessor(new MapAccessor())), Contexts.subDocPartContext(), @@ -292,7 +293,7 @@ private static Arguments repeatDocPartWithImagesInSourceTestshouldReplicateImage private static Arguments repeatDocPartTest() { return of("repeatDocPartTest", - new DocxStamperConfiguration(), + Configurations.standard(), new Characters(List.of(new Role("Homer Simpson", "Dan Castellaneta"), new Role("Marge Simpson", @@ -337,7 +338,7 @@ private static Arguments repeatDocPartTest() { private static Arguments repeatDocPartNestingTest() { return of("repeatDocPartNestingTest", - new DocxStamperConfiguration(), + Configurations.standard(), Contexts.schoolContext(), getResource(Path.of("RepeatDocPartNestingTest.docx")), """ @@ -524,9 +525,9 @@ private static Arguments repeatDocPartAndCommentProcessorsIsolationTest_repeatDo This will stay untouched too."""; - var config = new DocxStamperConfiguration(); - config.setEvaluationContextConfigurer((ctx) -> ctx.addPropertyAccessor( - new MapAccessor())); + var config = Configurations.standard() + .setEvaluationContextConfigurer( + (ctx) -> ctx.addPropertyAccessor(new MapAccessor())); return arguments( "RepeatDocPartAndCommentProcessorsIsolationTest_repeatDocPartShouldNotUseSameCommentProcessorInstancesForSubtemplate", @@ -539,8 +540,9 @@ private static Arguments repeatDocPartAndCommentProcessorsIsolationTest_repeatDo private static Arguments changingPageLayoutTest_shouldKeepSectionBreakOrientationInRepeatParagraphWithoutSectionBreakInsideComment() { return arguments( "changingPageLayoutTest_shouldKeepSectionBreakOrientationInRepeatParagraphWithoutSectionBreakInsideComment", - new DocxStamperConfiguration().setEvaluationContextConfigurer( - ctx -> ctx.addPropertyAccessor(new MapAccessor())), + Configurations.standard() + .setEvaluationContextConfigurer( + ctx -> ctx.addPropertyAccessor(new MapAccessor())), Map.of("repeatValues", List.of(new Name("Homer"), new Name("Marge"))), getResource(Path.of( @@ -578,8 +580,9 @@ Second page is portrait, layout change should survive to repeatParagraph process ❬❘docGrid=xxx,eGHdrFtrReferences=xxx,pgMar=xxx,pgSz={h=11906,orient=landscape,w=16838}❭ Fourth page is set to portrait again."""; - var config = new DocxStamperConfiguration().setEvaluationContextConfigurer( - ctx -> ctx.addPropertyAccessor(new MapAccessor())); + var config = Configurations.standard() + .setEvaluationContextConfigurer( + ctx -> ctx.addPropertyAccessor(new MapAccessor())); return arguments( "changingPageLayoutTest_shouldKeepSectionBreakOrientationInRepeatParagraphWithSectionBreakInsideComment", config, @@ -592,8 +595,9 @@ Second page is portrait, layout change should survive to repeatParagraph process private static Arguments changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithSectionBreaksInsideComment() { return arguments( "changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithSectionBreaksInsideComment", - new DocxStamperConfiguration().setEvaluationContextConfigurer( - ctx -> ctx.addPropertyAccessor(new MapAccessor())), + Configurations.standard() + .setEvaluationContextConfigurer( + ctx -> ctx.addPropertyAccessor(new MapAccessor())), Map.of("repeatValues", List.of(new Name("Homer"), new Name("Marge"))), getResource(Path.of("ChangingPageLayoutInRepeatDocPartTest" + @@ -617,8 +621,9 @@ Second page is landscape, layout change should survive to repeatDocPart (Marge). private static Arguments changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithSectionBreaksInsideCommentAndTableAsLastElement() { return arguments( "changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithSectionBreaksInsideCommentAndTableAsLastElement", - new DocxStamperConfiguration().setEvaluationContextConfigurer( - ctx -> ctx.addPropertyAccessor(new MapAccessor())), + Configurations.standard() + .setEvaluationContextConfigurer( + ctx -> ctx.addPropertyAccessor(new MapAccessor())), Map.of("repeatValues", List.of(new Name("Homer"), new Name("Marge"))), getResource( @@ -647,8 +652,9 @@ Second page is landscape, layout change should survive to repeatDocPart (Marge). private static Arguments changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithoutSectionBreaksInsideComment() { return arguments( "changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithoutSectionBreaksInsideComment", - new DocxStamperConfiguration().setEvaluationContextConfigurer( - ctx -> ctx.addPropertyAccessor(new MapAccessor())), + Configurations.standard() + .setEvaluationContextConfigurer( + ctx -> ctx.addPropertyAccessor(new MapAccessor())), Map.of("repeatValues", List.of(new Name("Homer"), new Name("Marge"))), getResource(Path.of( @@ -685,7 +691,7 @@ private static Arguments conditionalDisplayOfParagraphsTest_processorExpressions return arguments( "conditionalDisplayOfParagraphsTest_processorExpressionsInCommentsAreResolved", - new DocxStamperConfiguration(), + Configurations.standard(), context, template, expected); @@ -709,7 +715,7 @@ private static Arguments conditionalDisplayOfParagraphsTest_inlineProcessorExpre """; return arguments( "conditionalDisplayOfParagraphsTest_inlineProcessorExpressionsAreResolved", - new DocxStamperConfiguration(), + Configurations.standard(), context, template, expected); @@ -735,7 +741,7 @@ private static Arguments conditionalDisplayOfParagraphsTest_unresolvedInlineProc """; return arguments( "conditionalDisplayOfParagraphsTest_unresolvedInlineProcessorExpressionsAreRemoved", - new DocxStamperConfiguration(), + Configurations.standard(), context, template, expected); @@ -755,7 +761,7 @@ private static Arguments conditionalDisplayOfTableRowsTest() { ❬❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; return arguments("conditionalDisplayOfTableRowsTest", - new DocxStamperConfiguration(), + Configurations.standard(), context, template, expected); @@ -779,7 +785,7 @@ private static Arguments conditionalDisplayOfTablesBug32Test() { ❬❬This paragraph stays untouched.❘lang=de-DE❭❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; return arguments("conditionalDisplayOfTablesBug32Test", - new DocxStamperConfiguration(), + Configurations.standard(), context, template, expected); @@ -803,7 +809,7 @@ private static Arguments conditionalDisplayOfTablesTest() { ❬❬This paragraph stays untouched.❘lang=de-DE❭❘lang=de-DE,spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; return arguments("conditionalDisplayOfTablesTest", - new DocxStamperConfiguration(), + Configurations.standard(), context, template, expected); @@ -817,9 +823,11 @@ private static Arguments customEvaluationContextConfigurerTest_customEvaluationC ❬Custom EvaluationContextConfigurer Test❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ ❬❬This paragraph stays untouched.❘lang=de-DE❭❘lang=de-DE❭ ❬The variable foo has the value ❬bar❘lang=de-DE❭.❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; - var config = new DocxStamperConfiguration(); - config.setEvaluationContextConfigurer(evalContext -> evalContext.addPropertyAccessor( - new SimpleGetter("foo", "bar"))); + var config = Configurations.standard() + .setEvaluationContextConfigurer( + evalContext -> evalContext.addPropertyAccessor(new SimpleGetter( + "foo", + "bar"))); return arguments( "customEvaluationContextConfigurerTest_customEvaluationContextConfigurerIsHonored", @@ -837,9 +845,10 @@ private static Arguments customExpressionFunctionTest() { ❬❬This paragraph is untouched.❘lang=de-DE❭❘lang=de-DE❭ ❬❬In this paragraph, a custom expression function is used to uppercase a String: |BR|❘lang=de-DE❭❬HOMER SIMPSON❘b=true,lang=de-DE❭❬.❘lang=de-DE❭❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭ ❬❬To test that custom functions work together with comment expressions, we toggle visibility of this paragraph with a comment expression.❘lang=de-DE❭❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; - var config = new DocxStamperConfiguration().exposeInterfaceToExpressionLanguage( - Functions.UppercaseFunction.class, - Functions.upperCase()); + var config = Configurations.standard() + .exposeInterfaceToExpressionLanguage( + Functions.UppercaseFunction.class, + Functions.upperCase()); return arguments("customExpressionFunctionTest", config, context, @@ -849,7 +858,7 @@ private static Arguments customExpressionFunctionTest() { private static Arguments customTypeResolverTest() { return arguments("customTypeResolverTest", - new DocxStamperConfiguration() + Configurations.standard() .addResolver(new CustomTypeResolver()), new Context(new CustomType()), getResource(Path.of("CustomTypeResolverTest.docx")), @@ -870,7 +879,7 @@ private static Arguments dateReplacementTest() { Today is: %s""".formatted(formattedDate); return arguments("dateReplacementTest", - new DocxStamperConfiguration(), + Configurations.standard(), context, template, expected); @@ -885,8 +894,8 @@ private static Arguments expressionReplacementInGlobalParagraphsTest() { ❬❬This paragraph is untouched.❘lang=de-DE❭❘lang=de-DE❭ ❬In this paragraph, the variable ❘lang=de-DE❭❬name❘b=true,lang=de-DE❭ should be resolved to the value ❬Homer Simpson❘lang=de-DE❭. ❬❬In this paragraph, the variable ❘lang=de-DE❭❬foo❘b=true,lang=de-DE❭❬ should not be resolved: ${foo}.❘lang=de-DE❭❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; - DocxStamperConfiguration config = new DocxStamperConfiguration().setFailOnUnresolvedExpression( - false); + DocxStamperConfiguration config = Configurations.standard() + .setFailOnUnresolvedExpression(false); return arguments("expressionReplacementInGlobalParagraphsTest", config, context, @@ -912,8 +921,8 @@ private static Arguments expressionReplacementInTablesTest() { ${foo} ❬❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; - DocxStamperConfiguration config = new DocxStamperConfiguration().setFailOnUnresolvedExpression( - false); + DocxStamperConfiguration config = Configurations.standard() + .setFailOnUnresolvedExpression(false); return arguments("expressionReplacementInTablesTest", config, context, @@ -944,7 +953,7 @@ private static Arguments expressionReplacementWithFormattingTest() { ❬It should be white over darkblue: ❬Homer Simpson❘color=FFFFFF,highlight=darkBlue❭❘b=true❭ ❬It should be with header formatting: ❬Homer Simpson❘rStyle=TitreCar❭❘b=true❭"""; return arguments("expressionReplacementWithFormattingTest", - new DocxStamperConfiguration(), + Configurations.standard(), context, template, expected); @@ -965,7 +974,7 @@ private static Arguments expressionWithSurroundingSpacesTest() { Before Expression After. ❬Before Expression After.❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; return arguments("expressionWithSurroundingSpacesTest", - new DocxStamperConfiguration(), + Configurations.standard(), spacyContext, template, expected); @@ -980,8 +989,8 @@ private static Arguments expressionReplacementWithCommentsTest() { This paragraph is untouched. In this paragraph, the variable ❬name❘b=true❭ should be resolved to the value Homer Simpson. ❬In this paragraph, the variable ❬foo❘b=true❭ should not be resolved: unresolvedValueWithCommentreplaceWordWith(foo).❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; - var config = new DocxStamperConfiguration().setFailOnUnresolvedExpression( - false); + var config = Configurations.standard() + .setFailOnUnresolvedExpression(false); return arguments("expressionReplacementWithCommentsTest", config, context, @@ -1003,7 +1012,7 @@ private static Arguments imageReplacementInGlobalParagraphsTest() { ❬In this paragraph, an image of Mona Lisa is inserted: ❬rId4:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:1276350❘lang=de-DE❭.❘lang=de-DE❭ ❬This paragraph has the image ❬rId5:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:1276350❘lang=de-DE❭ in the middle.❘lang=de-DE,spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; return arguments("imageReplacementInGlobalParagraphsTest", - new DocxStamperConfiguration(), + Configurations.standard(), context, template, expected); @@ -1021,7 +1030,7 @@ private static Arguments imageReplacementInGlobalParagraphsTestWithMaxWidth() { ❬In this paragraph, an image of Mona Lisa is inserted: ❬rId4:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:635000❘lang=de-DE❭.❘lang=de-DE❭ ❬This paragraph has the image ❬rId5:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:635000❘lang=de-DE❭ in the middle.❘lang=de-DE,spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; return arguments("imageReplacementInGlobalParagraphsTestWithMaxWidth", - new DocxStamperConfiguration(), + Configurations.standard(), context, template, expected); @@ -1042,8 +1051,8 @@ private static Arguments leaveEmptyOnExpressionErrorTest() { var expected = """ Leave me empty . ❬❘u=single❭"""; - var config = new DocxStamperConfiguration().setFailOnUnresolvedExpression( - false) + var config = Configurations.standard() + .setFailOnUnresolvedExpression(false) .leaveEmptyOnExpressionError(true); return arguments("leaveEmptyOnExpressionErrorTest", config, @@ -1053,8 +1062,8 @@ private static Arguments leaveEmptyOnExpressionErrorTest() { } private static Arguments lineBreakReplacementTest() { - var config = new DocxStamperConfiguration(); - config.setLineBreakPlaceholder("#"); + var config = Configurations.standard() + .setLineBreakPlaceholder("#"); var context = new Contexts.Name(null); var template = getResource(Path.of("LineBreakReplacementTest.docx")); var expected = """ @@ -1093,7 +1102,7 @@ private static Arguments mapAccessorAndReflectivePropertyAccessorTest_shouldReso Paragraph end """; - var config = new DocxStamperConfiguration() + var config = Configurations.standard() .setFailOnUnresolvedExpression(false) .setLineBreakPlaceholder("\n") .addResolver(Resolvers.nullToDefault("N/C")) @@ -1127,8 +1136,8 @@ private static Arguments nullPointerResolutionTest_testWithDefaultSpel() { Deal with: ${nullish.li[2] ?: "Nullish value!!"} """; - var config = new DocxStamperConfiguration().setFailOnUnresolvedExpression( - false); + var config = Configurations.standard() + .setFailOnUnresolvedExpression(false); return arguments("nullPointerResolutionTest_testWithDefaultSpel", config, @@ -1140,7 +1149,7 @@ private static Arguments nullPointerResolutionTest_testWithDefaultSpel() { private static Arguments customCommentProcessor() { return arguments( "customCommentProcessor", - new DocxStamperConfiguration() + Configurations.standard() .addCommentProcessor( ICustomCommentProcessor.class, CustomCommentProcessor::new), @@ -1173,7 +1182,7 @@ private static Arguments nullPointerResolutionTest_testWithCustomSpel() { // Beware, this configuration only autogrows pojos and java beans, // so it will not work if your type has no default constructor and no setters. - var config = new DocxStamperConfiguration() + var config = Configurations.standard() .setSpelParserConfiguration(new SpelParserConfiguration(true, true)) .setEvaluationContextConfigurer(EvaluationContextConfigurers.noopConfigurer()) diff --git a/src/test/java/org/wickedsource/docxstamper/test/FailOnUnresolvedPlaceholderTest.java b/src/test/java/org/wickedsource/docxstamper/test/FailOnUnresolvedPlaceholderTest.java index 0af188e4..6f22e36a 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/FailOnUnresolvedPlaceholderTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/FailOnUnresolvedPlaceholderTest.java @@ -1,16 +1,15 @@ package org.wickedsource.docxstamper.test; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import org.wickedsource.docxstamper.DocxStamper; -import org.wickedsource.docxstamper.DocxStamperConfiguration; import pro.verron.docxstamper.api.DocxStamperException; +import pro.verron.docxstamper.preset.Configurations; +import pro.verron.docxstamper.test.utils.TestDocxStamper; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.Path; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.wickedsource.docxstamper.test.DefaultTests.getResource; @@ -24,12 +23,11 @@ void fails() throws IOException { var context = new Name("Homer"); try (var template = getResource(Path.of("FailOnUnresolvedExpressionTest" + ".docx"))) { - var config = new DocxStamperConfiguration() + var config = Configurations.standard() .setFailOnUnresolvedExpression(true); - var stamper = new DocxStamper(config); - var outputStream = new ByteArrayOutputStream(); + var stamper = new TestDocxStamper<>(config); assertThrows(DocxStamperException.class, - () -> stamper.stamp(template, context, outputStream)); + () -> stamper.stampAndLoad(template, context)); } } @@ -38,12 +36,10 @@ void doesNotFail() throws IOException { Name context = new Name("Homer"); try (InputStream template = getResource(Path.of( "FailOnUnresolvedExpressionTest.docx"))) { - var config = new DocxStamperConfiguration() + var config = Configurations.standard() .setFailOnUnresolvedExpression(false); - var stamper = new DocxStamper(config); - Assertions.assertDoesNotThrow(() -> stamper.stamp(template, - context, - new ByteArrayOutputStream())); + var stamper = new TestDocxStamper<>(config); + assertDoesNotThrow(() -> stamper.stampAndLoad(template, context)); } } diff --git a/src/test/java/org/wickedsource/docxstamper/test/MultiSectionTest.java b/src/test/java/org/wickedsource/docxstamper/test/MultiSectionTest.java index cad5ba8a..6ff1c5eb 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/MultiSectionTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/MultiSectionTest.java @@ -1,7 +1,7 @@ package org.wickedsource.docxstamper.test; import org.junit.jupiter.api.Test; -import org.wickedsource.docxstamper.DocxStamperConfiguration; +import pro.verron.docxstamper.preset.Configurations; import pro.verron.docxstamper.test.utils.TestDocxStamper; import java.nio.file.Path; @@ -18,16 +18,15 @@ class MultiSectionTest { void expressionsInMultipleSections() { var context = new NamesContext("Homer", "Marge"); var template = getResource(Path.of("MultiSectionTest.docx")); - var stamper = new TestDocxStamper( - new DocxStamperConfiguration()); + var configuration = Configurations.standard(); + var stamper = new TestDocxStamper(configuration); var actual = stamper.stampAndLoadAndExtract(template, context); String expected = """ Homer ❬❘docGrid=xxx,eGHdrFtrReferences=xxx,pgMar=xxx,pgSz={h=16838,w=11906}❭ Marge"""; - assertEquals(expected, - actual); + assertEquals(expected, actual); } diff --git a/src/test/java/org/wickedsource/docxstamper/test/MultiStampTest.java b/src/test/java/org/wickedsource/docxstamper/test/MultiStampTest.java index 196d39ff..344382af 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/MultiStampTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/MultiStampTest.java @@ -1,7 +1,7 @@ package org.wickedsource.docxstamper.test; import org.junit.jupiter.api.Test; -import org.wickedsource.docxstamper.DocxStamperConfiguration; +import pro.verron.docxstamper.preset.Configurations; import pro.verron.docxstamper.test.utils.TestDocxStamper; import java.nio.file.Path; @@ -17,7 +17,7 @@ class MultiStampTest { @Test void expressionsAreResolvedOnMultiStamp() { - var config = new DocxStamperConfiguration(); + var config = Configurations.standard(); var context = names("Homer","Marge","Bart","Lisa","Maggie"); var stamper = new TestDocxStamper<>(config); diff --git a/src/test/java/org/wickedsource/docxstamper/test/NullPointerResolutionTest.java b/src/test/java/org/wickedsource/docxstamper/test/NullPointerResolutionTest.java index 04f76534..bc4f5790 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/NullPointerResolutionTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/NullPointerResolutionTest.java @@ -1,8 +1,8 @@ package org.wickedsource.docxstamper.test; import org.junit.jupiter.api.Test; -import org.wickedsource.docxstamper.DocxStamperConfiguration; import pro.verron.docxstamper.api.DocxStamperException; +import pro.verron.docxstamper.preset.Configurations; import pro.verron.docxstamper.test.utils.TestDocxStamper; import pro.verron.docxstamper.test.utils.context.NullishContext; import pro.verron.docxstamper.test.utils.context.SubContext; @@ -26,8 +26,8 @@ void nullPointerResolutionTest_testThrowingCase() throws IOException { var context = new NullishContext("Fullish1", subContext, null, null); try (var template = getResource(Path.of("NullPointerResolution.docx"))) { - var stamper = new TestDocxStamper( - new DocxStamperConfiguration()); + var configuration = Configurations.standard(); + var stamper = new TestDocxStamper(configuration); assertThrows( DocxStamperException.class, () -> stamper.stampAndLoadAndExtract(template, context) diff --git a/src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java b/src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java index a15c17ec..dd990533 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java @@ -1,7 +1,7 @@ package org.wickedsource.docxstamper.test; import org.junit.jupiter.api.Test; -import org.wickedsource.docxstamper.DocxStamperConfiguration; +import pro.verron.docxstamper.preset.Configurations; import pro.verron.docxstamper.test.utils.TestDocxStamper; import java.nio.file.Path; @@ -19,8 +19,9 @@ void expressionReplacementInHeaderAndFooterTest() { var context = new Name("Homer Simpson"); var template = getResource( Path.of("ExpressionReplacementInHeaderAndFooterTest.docx")); - var stamper = new TestDocxStamper(new DocxStamperConfiguration().setFailOnUnresolvedExpression( - false)); + var configuration = Configurations.standard() + .setFailOnUnresolvedExpression(false); + var stamper = new TestDocxStamper(configuration); var actual = stamper.stampAndLoadAndExtract(template, context); assertEquals(""" ❬❬This ❘lang=de-DE❭❬header ❘lang=de-DE❭❬paragraph is untouched.❘lang=de-DE❭❘lang=de-DE❭ diff --git a/src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java b/src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java index 27a93c5e..c08a6d58 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java @@ -2,7 +2,7 @@ import org.docx4j.dml.wordprocessingDrawing.Anchor; import org.junit.jupiter.api.Test; -import org.wickedsource.docxstamper.DocxStamperConfiguration; +import pro.verron.docxstamper.preset.Configurations; import pro.verron.docxstamper.test.utils.TestDocxStamper; import java.nio.file.Path; @@ -21,7 +21,9 @@ void expressionReplacementInTextBoxesTest() { var context = new Name("Bart Simpson"); var template = getResource(Path.of("ExpressionReplacementInTextBoxesTest" + ".docx")); - var stamper = new TestDocxStamper(new DocxStamperConfiguration().setFailOnUnresolvedExpression(false)); + var configuration = Configurations.standard() + .setFailOnUnresolvedExpression(false); + var stamper = new TestDocxStamper(configuration); var actual = stamper.stampAndLoadAndExtract(template, context, Anchor.class); List expected = List.of( "❬Bart Simpson❘color=auto❭", diff --git a/src/test/java/org/wickedsource/docxstamper/test/RepeatDocPartBadPlaceholderTest.java b/src/test/java/org/wickedsource/docxstamper/test/RepeatDocPartBadPlaceholderTest.java index 8c2cf8bd..0fa628e4 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/RepeatDocPartBadPlaceholderTest.java +++ b/src/test/java/org/wickedsource/docxstamper/test/RepeatDocPartBadPlaceholderTest.java @@ -4,8 +4,8 @@ import org.junit.jupiter.api.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.wickedsource.docxstamper.DocxStamperConfiguration; import pro.verron.docxstamper.api.DocxStamperException; +import pro.verron.docxstamper.preset.Configurations; import pro.verron.docxstamper.test.utils.TestDocxStamper; import pro.verron.docxstamper.test.utils.context.Contexts.Characters; import pro.verron.docxstamper.test.utils.context.Contexts.Role; @@ -36,7 +36,8 @@ public void testBadExpressionShouldNotBlockCallerThread() { List.of(new Role("Homer Simpson", "Dan Castellaneta"), new Role("Marge Simpson", "Julie Kavner"), new Role("Bart Simpson", "Nancy Cartwright"))); - var stamper = new TestDocxStamper<>(new DocxStamperConfiguration()); + var configuration = Configurations.standard(); + var stamper = new TestDocxStamper<>(configuration); var exception = assertThrows( DocxStamperException.class, diff --git a/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java b/src/test/java/pro/verron/docxstamper/test/CustomCommentProcessor.java similarity index 98% rename from src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java rename to src/test/java/pro/verron/docxstamper/test/CustomCommentProcessor.java index 97c6f376..7e775789 100644 --- a/src/test/java/pro/verron/docxstamper/test/commentProcessors/CustomCommentProcessor.java +++ b/src/test/java/pro/verron/docxstamper/test/CustomCommentProcessor.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test.commentProcessors; +package pro.verron.docxstamper.test; import org.docx4j.jaxb.Context; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; diff --git a/src/test/java/pro/verron/docxstamper/test/commentProcessors/ICustomCommentProcessor.java b/src/test/java/pro/verron/docxstamper/test/ICustomCommentProcessor.java similarity index 85% rename from src/test/java/pro/verron/docxstamper/test/commentProcessors/ICustomCommentProcessor.java rename to src/test/java/pro/verron/docxstamper/test/ICustomCommentProcessor.java index 262031e8..ec85c1af 100644 --- a/src/test/java/pro/verron/docxstamper/test/commentProcessors/ICustomCommentProcessor.java +++ b/src/test/java/pro/verron/docxstamper/test/ICustomCommentProcessor.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test.commentProcessors; +package pro.verron.docxstamper.test; import pro.verron.docxstamper.api.CommentProcessor; diff --git a/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java b/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java index c3ae2021..b68f9b28 100644 --- a/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java +++ b/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java @@ -1,8 +1,8 @@ package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; -import org.wickedsource.docxstamper.DocxStamperConfiguration; import pro.verron.docxstamper.api.DocxStamperException; +import pro.verron.docxstamper.preset.Configurations; import pro.verron.docxstamper.test.utils.TestDocxStamper; import pro.verron.docxstamper.test.utils.context.Contexts; @@ -22,7 +22,8 @@ class SpelInjectionTest { void spelInjectionTest() throws IOException { var context = Contexts.empty(); try (var template = getResource(Path.of("SpelInjectionTest.docx"))) { - var stamper = new TestDocxStamper<>(new DocxStamperConfiguration()); + var configuration = Configurations.standard(); + var stamper = new TestDocxStamper<>(configuration); assertThrows(DocxStamperException.class, () -> stamper.stampAndLoadAndExtract(template, context)); } assertDoesNotThrow(() -> "Does not throw", "Since VM is still up."); diff --git a/src/test/java/pro/verron/docxstamper/test/StampTableTest.java b/src/test/java/pro/verron/docxstamper/test/StampTableTest.java index 10464526..e65eabdb 100644 --- a/src/test/java/pro/verron/docxstamper/test/StampTableTest.java +++ b/src/test/java/pro/verron/docxstamper/test/StampTableTest.java @@ -1,7 +1,7 @@ package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; -import org.wickedsource.docxstamper.DocxStamperConfiguration; +import pro.verron.docxstamper.preset.Configurations; import pro.verron.docxstamper.test.utils.TestDocxStamper; import pro.verron.docxstamper.test.utils.context.Contexts; @@ -21,7 +21,8 @@ void stampTableTest() { var testDocx = getResource(Path.of("StampTableTest.docx")); - var stamper = new TestDocxStamper<>(new DocxStamperConfiguration()); + var configuration = Configurations.standard(); + var stamper = new TestDocxStamper<>(configuration); String string = stamper.stampAndLoadAndExtract( testDocx, diff --git a/src/test/java/pro/verron/docxstamper/test/utils/TestDocxStamper.java b/src/test/java/pro/verron/docxstamper/test/utils/TestDocxStamper.java index 00f66315..ac3c18d9 100644 --- a/src/test/java/pro/verron/docxstamper/test/utils/TestDocxStamper.java +++ b/src/test/java/pro/verron/docxstamper/test/utils/TestDocxStamper.java @@ -9,14 +9,16 @@ import org.docx4j.openpackaging.parts.relationships.RelationshipsPart; import org.docx4j.relationships.Relationship; import org.docx4j.wml.P; -import org.wickedsource.docxstamper.DocxStamper; -import org.wickedsource.docxstamper.DocxStamperConfiguration; +import pro.verron.docxstamper.api.DocxStamperConfiguration; +import pro.verron.docxstamper.api.LoadingOpcStamper; +import pro.verron.docxstamper.preset.Stampers; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; import java.util.Optional; +import java.util.function.Function; import java.util.stream.Stream; import static java.util.stream.Collectors.joining; @@ -30,17 +32,24 @@ */ public final class TestDocxStamper { - private final DocxStamper stamper; + private final LoadingOpcStamper stamper; private WordprocessingMLPackage document; /** *

Constructor for TestDocxStamper.

* - * @param config a {@link org.wickedsource.docxstamper.DocxStamperConfiguration} object + * @param config a {@link DocxStamperConfiguration} object * @since 1.6.6 */ public TestDocxStamper(DocxStamperConfiguration config) { - stamper = new DocxStamper<>(config); + Function loader = inputStream -> { + try { + return WordprocessingMLPackage.load(inputStream); + } catch (Docx4JException e) { + throw new RuntimeException(e); + } + }; + stamper = new LoadingOpcStamper<>(loader, Stampers.from(config)); } /** From cb889257642cb19771bd4cb44c17a3a5fb3aab4f Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Fri, 15 Mar 2024 07:37:20 +0100 Subject: [PATCH 035/134] Refactor code for correct visibility and typing The commit includes changes in multiple classes where the import statements have been updated, and the types of methods have been corrected from 'ICommentProcessor' to 'CommentProcessor'. Further, a visibility issue was addressed by extending the `PreProcessor` interface. By swapping out these dependencies for their appropriate counterparts, visibility and typing issues that initially existed in the codebase have been successfully remedied. --- .../api/preprocessor/PreProcessor.java | 10 ++-------- .../pro/verron/docxstamper/api/PreProcessor.java | 12 ++++++++++++ .../preset/CommentProcessorFactory.java | 15 ++++++++------- .../preset/EvaluationContextConfigurers.java | 3 ++- 4 files changed, 24 insertions(+), 16 deletions(-) create mode 100644 src/main/java/pro/verron/docxstamper/api/PreProcessor.java diff --git a/src/main/java/org/wickedsource/docxstamper/api/preprocessor/PreProcessor.java b/src/main/java/org/wickedsource/docxstamper/api/preprocessor/PreProcessor.java index 5f2ffc21..3e5abfb3 100644 --- a/src/main/java/org/wickedsource/docxstamper/api/preprocessor/PreProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/api/preprocessor/PreProcessor.java @@ -1,6 +1,5 @@ package org.wickedsource.docxstamper.api.preprocessor; -import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.wickedsource.docxstamper.DocxStamper; import org.wickedsource.docxstamper.preprocessor.MergeSameStyleRuns; import org.wickedsource.docxstamper.preprocessor.RemoveProofErrors; @@ -17,11 +16,6 @@ * @version ${version} * @since 1.6.4 */ -public interface PreProcessor { - /** - * Processes the given document before the actual processing takes place. - * - * @param document the document to process. - */ - void process(WordprocessingMLPackage document); +public interface PreProcessor + extends pro.verron.docxstamper.api.PreProcessor { } diff --git a/src/main/java/pro/verron/docxstamper/api/PreProcessor.java b/src/main/java/pro/verron/docxstamper/api/PreProcessor.java new file mode 100644 index 00000000..a78b9d85 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/api/PreProcessor.java @@ -0,0 +1,12 @@ +package pro.verron.docxstamper.api; + +import org.docx4j.openpackaging.packages.WordprocessingMLPackage; + +public interface PreProcessor { + /** + * Processes the given document before the actual processing takes place. + * + * @param document the document to process. + */ + void process(WordprocessingMLPackage document); +} diff --git a/src/main/java/pro/verron/docxstamper/preset/CommentProcessorFactory.java b/src/main/java/pro/verron/docxstamper/preset/CommentProcessorFactory.java index c8fe1c3a..17caeebb 100644 --- a/src/main/java/pro/verron/docxstamper/preset/CommentProcessorFactory.java +++ b/src/main/java/pro/verron/docxstamper/preset/CommentProcessorFactory.java @@ -2,7 +2,6 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.wickedsource.docxstamper.DocxStamper; -import org.wickedsource.docxstamper.DocxStamperConfiguration; import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.displayif.DisplayIfProcessor; import org.wickedsource.docxstamper.processor.repeat.ParagraphRepeatProcessor; @@ -10,6 +9,8 @@ import org.wickedsource.docxstamper.processor.repeat.RepeatProcessor; import org.wickedsource.docxstamper.processor.replaceExpression.ReplaceWithProcessor; import org.wickedsource.docxstamper.processor.table.TableResolver; +import pro.verron.docxstamper.api.CommentProcessor; +import pro.verron.docxstamper.api.DocxStamperConfiguration; import pro.verron.docxstamper.api.OpcStamper; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.core.PlaceholderReplacer; @@ -39,7 +40,7 @@ public CommentProcessorFactory(DocxStamperConfiguration configuration) { * @param pr a {@link PlaceholderReplacer} object * @return a {@link ICommentProcessor} object */ - public ICommentProcessor repeatParagraph(ParagraphPlaceholderReplacer pr) { + public CommentProcessor repeatParagraph(ParagraphPlaceholderReplacer pr) { return ParagraphRepeatProcessor.newInstance(pr); } @@ -49,7 +50,7 @@ public ICommentProcessor repeatParagraph(ParagraphPlaceholderReplacer pr) { * @param pr a {@link PlaceholderReplacer} object * @return a {@link ICommentProcessor} object */ - public ICommentProcessor repeatDocPart(ParagraphPlaceholderReplacer pr) { + public CommentProcessor repeatDocPart(ParagraphPlaceholderReplacer pr) { return RepeatDocPartProcessor.newInstance(pr, getStamper()); } @@ -63,7 +64,7 @@ private OpcStamper getStamper() { * @param pr a {@link PlaceholderReplacer} object * @return a {@link ICommentProcessor} object */ - public ICommentProcessor repeat(ParagraphPlaceholderReplacer pr) { + public CommentProcessor repeat(ParagraphPlaceholderReplacer pr) { return RepeatProcessor.newInstance(pr); } @@ -73,7 +74,7 @@ public ICommentProcessor repeat(ParagraphPlaceholderReplacer pr) { * @param pr a {@link PlaceholderReplacer} object * @return a {@link ICommentProcessor} object */ - public ICommentProcessor tableResolver(ParagraphPlaceholderReplacer pr) { + public CommentProcessor tableResolver(ParagraphPlaceholderReplacer pr) { return TableResolver.newInstance(pr); } @@ -83,7 +84,7 @@ public ICommentProcessor tableResolver(ParagraphPlaceholderReplacer pr) { * @param pr a {@link PlaceholderReplacer} object * @return a {@link ICommentProcessor} object */ - public ICommentProcessor displayIf(ParagraphPlaceholderReplacer pr) { + public CommentProcessor displayIf(ParagraphPlaceholderReplacer pr) { return DisplayIfProcessor.newInstance(pr); } @@ -93,7 +94,7 @@ public ICommentProcessor displayIf(ParagraphPlaceholderReplacer pr) { * @param pr a {@link PlaceholderReplacer} object * @return a {@link ICommentProcessor} object */ - public ICommentProcessor replaceWith(ParagraphPlaceholderReplacer pr) { + public CommentProcessor replaceWith(ParagraphPlaceholderReplacer pr) { return ReplaceWithProcessor.newInstance(pr); } } diff --git a/src/main/java/pro/verron/docxstamper/preset/EvaluationContextConfigurers.java b/src/main/java/pro/verron/docxstamper/preset/EvaluationContextConfigurers.java index 18f3fd51..854c051d 100644 --- a/src/main/java/pro/verron/docxstamper/preset/EvaluationContextConfigurers.java +++ b/src/main/java/pro/verron/docxstamper/preset/EvaluationContextConfigurers.java @@ -1,7 +1,8 @@ package pro.verron.docxstamper.preset; -import org.wickedsource.docxstamper.api.EvaluationContextConfigurer; + import org.wickedsource.docxstamper.el.NoOpEvaluationContextConfigurer; +import pro.verron.docxstamper.api.EvaluationContextConfigurer; public class EvaluationContextConfigurers { public static EvaluationContextConfigurer noopConfigurer() { From c067e60335882bca92d1b3065b8221930eb624e2 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Fri, 15 Mar 2024 07:37:38 +0100 Subject: [PATCH 036/134] Update module dependencies in test and main code Module names have been updated in the main and test code's module-info.java files. The previous module "pro.verron.opcstamper" is replaced with "pro.verron.docxstamper" to reflect the changes in the code and its dependencies. This will ensure that all dependencies are correctly loaded and used by both main and test code. --- src/main/java/module-info.java | 4 +--- src/test/java/module-info.java | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index e4b86c5d..dc25f550 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,4 +1,4 @@ -module pro.verron.opcstamper { +module pro.verron.docxstamper { requires spring.core; requires spring.expression; @@ -21,6 +21,4 @@ // exports org.wickedsource.docxstamper.util; // exports org.wickedsource.docxstamper.processor; // exports org.wickedsource.docxstamper.processor.table; - - } diff --git a/src/test/java/module-info.java b/src/test/java/module-info.java index b5fed305..cbc19d79 100644 --- a/src/test/java/module-info.java +++ b/src/test/java/module-info.java @@ -1,5 +1,5 @@ -module pro.verron.opcstamper.test { - requires transitive pro.verron.opcstamper; +module pro.verron.docxstamper.test { + requires transitive pro.verron.docxstamper; requires org.junit.jupiter.api; requires org.junit.jupiter.params; From 5bf7bd5e31f93239c182a53605600ecc6dc7092c Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Fri, 15 Mar 2024 07:45:41 +0100 Subject: [PATCH 037/134] Refactor ITypeResolver interface location and update its references The ITypeResolver interface has been moved from the 'org.wickedsource.docxstamper.api.typeresolver' package to the 'pro.verron.docxstamper.api' package. The references to it scattered across other classes have also been updated to its new location. Additionally, the usage of PreProcessor has been removed from several classes as part of the refactoring process. --- .../wickedsource/docxstamper/DocxStamper.java | 1 - .../docxstamper/DocxStamperConfiguration.java | 7 +------ .../api/typeresolver/ITypeResolver.java | 18 ++--------------- .../replace/typeresolver/TypeResolver.java | 2 +- .../api/DocxStamperConfiguration.java | 2 -- .../verron/docxstamper/api/ITypeResolver.java | 20 +++++++++++++++++++ 6 files changed, 24 insertions(+), 26 deletions(-) create mode 100644 src/main/java/pro/verron/docxstamper/api/ITypeResolver.java diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java index ffe259b5..b0ed348b 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java @@ -7,7 +7,6 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.lang.NonNull; import org.wickedsource.docxstamper.api.DocxStamperException; -import org.wickedsource.docxstamper.api.preprocessor.PreProcessor; import org.wickedsource.docxstamper.el.ExpressionResolver; import org.wickedsource.docxstamper.el.StandardMethodResolver; import org.wickedsource.docxstamper.processor.CommentProcessorRegistry; diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java index 929cb1eb..788cafa3 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java @@ -5,8 +5,6 @@ import org.springframework.expression.spel.SpelParserConfiguration; import org.springframework.lang.NonNull; import org.wickedsource.docxstamper.api.DocxStamperException; -import org.wickedsource.docxstamper.api.preprocessor.PreProcessor; -import org.wickedsource.docxstamper.api.typeresolver.ITypeResolver; import org.wickedsource.docxstamper.el.DefaultEvaluationContextConfigurer; import org.wickedsource.docxstamper.processor.displayif.IDisplayIfProcessor; import org.wickedsource.docxstamper.processor.repeat.IParagraphRepeatProcessor; @@ -15,10 +13,7 @@ import org.wickedsource.docxstamper.processor.replaceExpression.IReplaceWithProcessor; import org.wickedsource.docxstamper.processor.table.ITableResolver; import org.wickedsource.docxstamper.replace.typeresolver.TypeResolver; -import pro.verron.docxstamper.api.CommentProcessor; -import pro.verron.docxstamper.api.EvaluationContextConfigurer; -import pro.verron.docxstamper.api.ObjectResolver; -import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; +import pro.verron.docxstamper.api.*; import pro.verron.docxstamper.preset.CommentProcessorFactory; import pro.verron.docxstamper.preset.Resolvers; import pro.verron.docxstamper.preset.resolver.Null2DefaultResolver; diff --git a/src/main/java/org/wickedsource/docxstamper/api/typeresolver/ITypeResolver.java b/src/main/java/org/wickedsource/docxstamper/api/typeresolver/ITypeResolver.java index 1eebb260..cee619fd 100644 --- a/src/main/java/org/wickedsource/docxstamper/api/typeresolver/ITypeResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/api/typeresolver/ITypeResolver.java @@ -1,7 +1,5 @@ package org.wickedsource.docxstamper.api.typeresolver; -import org.docx4j.openpackaging.packages.WordprocessingMLPackage; -import org.docx4j.wml.R; import pro.verron.docxstamper.api.ObjectResolver; /** @@ -29,18 +27,6 @@ * and simplifies the internal workings of the docx-stamper project. */ @Deprecated(since = "1.6.7", forRemoval = true) -public interface ITypeResolver { - /** - * This method is called when an expression is found in the .docx template. - * It creates an object of the DOCX4J api in the place of the found expression. - * - * @param document the Word document that can be accessed via the - * DOCX4J api. - * @param expressionResult the result of an expression. Only objects of classes this type resolver is registered for - * within the TypeResolverRegistry are passed into this method. - * @return an object of the DOCX4J api (usually of type {@link R} = "run - * of text" that will be put in the place of an - * expression found in the .docx document. - */ - R resolve(WordprocessingMLPackage document, T expressionResult); +public interface ITypeResolver + extends pro.verron.docxstamper.api.ITypeResolver { } diff --git a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/TypeResolver.java b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/TypeResolver.java index 39a29533..e6c1147c 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/TypeResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/TypeResolver.java @@ -3,7 +3,7 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; import org.wickedsource.docxstamper.api.DocxStamperException; -import org.wickedsource.docxstamper.api.typeresolver.ITypeResolver; +import pro.verron.docxstamper.api.ITypeResolver; import pro.verron.docxstamper.api.ObjectResolver; /** diff --git a/src/main/java/pro/verron/docxstamper/api/DocxStamperConfiguration.java b/src/main/java/pro/verron/docxstamper/api/DocxStamperConfiguration.java index 92014fdd..9dad1d20 100644 --- a/src/main/java/pro/verron/docxstamper/api/DocxStamperConfiguration.java +++ b/src/main/java/pro/verron/docxstamper/api/DocxStamperConfiguration.java @@ -3,8 +3,6 @@ import org.springframework.expression.spel.SpelParserConfiguration; import org.springframework.lang.NonNull; import org.wickedsource.docxstamper.DocxStamper; -import org.wickedsource.docxstamper.api.preprocessor.PreProcessor; -import org.wickedsource.docxstamper.api.typeresolver.ITypeResolver; import java.util.List; import java.util.Map; diff --git a/src/main/java/pro/verron/docxstamper/api/ITypeResolver.java b/src/main/java/pro/verron/docxstamper/api/ITypeResolver.java new file mode 100644 index 00000000..5057976e --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/api/ITypeResolver.java @@ -0,0 +1,20 @@ +package pro.verron.docxstamper.api; + +import org.docx4j.openpackaging.packages.WordprocessingMLPackage; +import org.docx4j.wml.R; + +public interface ITypeResolver { + /** + * This method is called when an expression is found in the .docx template. + * It creates an object of the DOCX4J api in the place of the found expression. + * + * @param document the Word document that can be accessed via the + * DOCX4J api. + * @param expressionResult the result of an expression. Only objects of classes this type resolver is registered for + * within the TypeResolverRegistry are passed into this method. + * @return an object of the DOCX4J api (usually of type {@link R} = "run + * of text" that will be put in the place of an + * expression found in the .docx document. + */ + R resolve(WordprocessingMLPackage document, T expressionResult); +} From f07330208f34744f6beae1a43c9bf4c843aaed98 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Fri, 15 Mar 2024 08:26:22 +0100 Subject: [PATCH 038/134] Modify build method return type in DocxStamperConfiguration The return type of the build method in DocxStamperConfiguration has been changed to OpcStamper. This change respects the Java module visibility rules and ensures proper interaction between the DocxStamper and its configuration. Corresponding modifications were made in both pro.verron.docxstamper.api.DocxStamperConfiguration and org.wickedsource.docxstamper.DocxStamperConfiguration. --- .../wickedsource/docxstamper/DocxStamperConfiguration.java | 2 +- .../pro/verron/docxstamper/api/DocxStamperConfiguration.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java index 788cafa3..b13ea7d9 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java @@ -265,7 +265,7 @@ public DocxStamperConfiguration addCommentProcessor( */ @Override @Deprecated(forRemoval = true, since = "1.6.4") - public DocxStamper build() { + public OpcStamper build() { return new DocxStamper<>(this); } diff --git a/src/main/java/pro/verron/docxstamper/api/DocxStamperConfiguration.java b/src/main/java/pro/verron/docxstamper/api/DocxStamperConfiguration.java index 9dad1d20..2a52b1f7 100644 --- a/src/main/java/pro/verron/docxstamper/api/DocxStamperConfiguration.java +++ b/src/main/java/pro/verron/docxstamper/api/DocxStamperConfiguration.java @@ -1,8 +1,8 @@ package pro.verron.docxstamper.api; +import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.springframework.expression.spel.SpelParserConfiguration; import org.springframework.lang.NonNull; -import org.wickedsource.docxstamper.DocxStamper; import java.util.List; import java.util.Map; @@ -48,7 +48,7 @@ DocxStamperConfiguration addCommentProcessor( ); @Deprecated(forRemoval = true, since = "1.6.4") - DocxStamper build(); + OpcStamper build(); void addPreprocessor(PreProcessor preprocessor); From 14aa56317b4b029472ad09a1aebaab54487f8998 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Fri, 15 Mar 2024 08:31:56 +0100 Subject: [PATCH 039/134] Revise comments and strings for grammar and clarity This commit corrects minor grammatical and punctuation inconsistencies across various comment lines and method names in different files. It also updates some strings for better readability and understanding, and reformat some comments to improve clarity. Modification includes corrections in CONTRIBUTING.md, several Java files, and README.adoc. --- .devcontainer/devcontainer.json | 3 +-- .github/dependabot.yml | 2 +- CONTRIBUTING.md | 14 +++++++++----- README.adoc | 8 ++++---- .../docxstamper/processor/table/TableResolver.java | 2 +- .../replace/typeresolver/image/Image.java | 4 ++-- .../org/wickedsource/docxstamper/util/RunUtil.java | 5 +++-- .../verron/docxstamper/api/CommentProcessor.java | 6 +++--- .../pro/verron/docxstamper/api/ITypeResolver.java | 2 +- .../java/pro/verron/docxstamper/api/Image.java | 4 ++-- .../pro/verron/docxstamper/core/CommentUtil.java | 2 +- .../verron/docxstamper/core/DefaultParagraph.java | 2 +- .../docxstamper/test/DefaultTests.java | 6 +++--- 13 files changed, 32 insertions(+), 28 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 7317b70e..0bbdded0 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,5 +1,4 @@ -// For format details, see https://aka.ms/devcontainer.json. For config options, see the -// README at: https://github.com/devcontainers/templates/tree/main/src/java +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: https://github.com/devcontainers/templates/tree/main/src/java { "name": "Java", // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5c18bb79..6b39853d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,4 +1,4 @@ -# To get started with Dependabot version updates, you'll need to specify which package ecosystems to update and where the package manifests are located. +# To get started with Dependabot version updates, you'll need to specify which package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f2cf38cf..4bfd1a84 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,7 @@ All types of contributions are encouraged and valued. See the [Table of Contents > the project and show your appreciation, which we would also be thrilled about: > - Star the project > - Tweet about it -> - Refer this project in your project's readme +> - Refer to this project in your project's readme > - Mention the project at local meetups and tell your friends/colleagues @@ -52,7 +52,7 @@ We will then take care of the issue as soon as possible. #### How Do I Submit a Good Bug Report? -> You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue +> You must never report security related issues, vulnerabilities or bugs, including sensitive information to the issue > tracker, or elsewhere in public. Instead, sensitive bugs must be sent by email to security@verron.pro. @@ -99,7 +101,9 @@ We use GitHub issues to track bugs and errors. If you run into an issue with the - Open an [Issue](https://github.com/verronpro/docx-stamper/issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.) - Explain the behavior you would expect and the actual behavior. -- Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case. +- Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to + recreate the issue on their own. This usually includes your code. For good bug reports, you should isolate the problem + and create a reduced test case. - Provide the information you collected in the previous section. Once it's filed: diff --git a/README.adoc b/README.adoc index 8264db13..3023e6de 100644 --- a/README.adoc +++ b/README.adoc @@ -39,7 +39,7 @@ The main feature of docx-stamper is *replacement of expressions* within the text Simply add expressions like `${person.name}` or `${person.name.equals("Homer") ? "Duff" : "Budweiser"}` in the text of your .docx template and provide a context object against which the placeholder can be resolved. docx-stamper will try to keep the original formatting of the text in the template intact. You can use the full feature set of http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html[Spring Expression Language] (SpEL). -The value that an placeholder resolves to may be of the following types: +The value that a placeholder resolves to may be of the following types: .Type that can be resolved to an element in the .docx document [cols=">1,3"] @@ -54,7 +54,7 @@ The value that an placeholder resolves to may be of the following types: | `link:{repo}/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java[org.wickedsource...Image]` |The placeholder is replaced with an inline image. |=== -If an placeholder cannot be resolved successfully, it will be skipped (meaning the placeholder stays in the document as it was in the template). +If a placeholder cannot be resolved successfully, it will be skipped (meaning the placeholder stays in the document as it was in the template). To support more than the above types, you can implement your own `link:{repo}/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java[ObjectResolver]`. To register your it with docx-stamper, use the following code: @@ -171,7 +171,7 @@ The expression will be evaluated as it would be in a comment. == Error Handling -By default, DocxStamper fails with an UnresolvedExpressionException if an placeholder within the document or within the comments cannot be resolved successfully. +By default, DocxStamper fails with an UnresolvedExpressionException if a placeholder within the document or within the comments cannot be resolved successfully. If you want to change this behavior, you can do the following: [source,java] @@ -215,4 +215,4 @@ This way, you can choose which version of Docx4J you want to use instead of havi == Contribute -If you have an issue or created a comment processor or type resolver that you think deserves to be part of the default distribution, feel free to open an issue or - even better - a pull request with your contribution. +If you have an issue or create a comment processor or type resolver that you think deserves to be part of the default distribution, feel free to open an issue or - even better - a pull request with your contribution. diff --git a/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java b/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java index 67e9dcb5..f5bab6cc 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java @@ -127,7 +127,7 @@ private void growAndFillRow(Tr row, List values) { setCellText(cell0tc, values.isEmpty() ? "" : values.get(0)); if (values.size() > 1) { - //Copy first cell and replace content for each remaining values + //Copy the first cell and replace content for each remaining value for (String cellContent : values.subList(1, values.size())) { JAXBElement xmlCell = XmlUtils.deepCopy(cell0); setCellText(xmlCell.getValue(), cellContent); diff --git a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java index 2478ad46..828ff5ca 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java @@ -39,7 +39,7 @@ public Image(InputStream in, Integer maxWidth) throws IOException { /** *

Constructor for Image.

* - * @param imageBytes - content of the image as array of the bytes + * @param imageBytes - content of the image as an array of the bytes */ public Image(byte[] imageBytes) { super(imageBytes); @@ -48,7 +48,7 @@ public Image(byte[] imageBytes) { /** *

Constructor for Image.

* - * @param imageBytes - content of the image as array of the bytes + * @param imageBytes - content of the image as an array of the bytes * @param maxWidth - max width of the image in twip */ public Image(byte[] imageBytes, Integer maxWidth) { diff --git a/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java b/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java index efc45f07..9694c78d 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java +++ b/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java @@ -157,8 +157,9 @@ public static R createRunWithImage( Integer maxWidth, BinaryPartAbstractImage abstractImage ) { - // creating random ids assuming they are unique - // id must not be too large, otherwise Word cannot open the document + // creating random ids assuming they are unique, + // id must not be too large; + // otherwise Word cannot open the document int id1 = random.nextInt(100000); int id2 = random.nextInt(100000); var filenameHint = "dummyFileName"; diff --git a/src/main/java/pro/verron/docxstamper/api/CommentProcessor.java b/src/main/java/pro/verron/docxstamper/api/CommentProcessor.java index 06d0e143..9013a63f 100644 --- a/src/main/java/pro/verron/docxstamper/api/CommentProcessor.java +++ b/src/main/java/pro/verron/docxstamper/api/CommentProcessor.java @@ -26,7 +26,7 @@ public interface CommentProcessor { void setParagraph(P paragraph); /** - * Passes the run that is currently being processed (i.e. the run that is commented in the + * Passes the run that is currently being processed (i.e., the run that is commented in the * .docx template). This method is always called BEFORE the custom * methods of the custom comment processor interface * are called. @@ -37,7 +37,7 @@ public interface CommentProcessor { /** * Passes the comment range wrapper that is currently being processed - * (i.e. the start and end of comment that in the .docx template). + * (i.e., the start and end of comment that in the .docx template). * This method is always called BEFORE the custom methods of the custom comment * processor interface are called. * @@ -46,7 +46,7 @@ public interface CommentProcessor { void setCurrentCommentWrapper(CommentWrapper commentWrapper); /** - * Passes the processed document, in order to make all linked data + * Passes the processed document, to make all linked data * (images, etc.) available * to processors that need it (example: repeatDocPart) * diff --git a/src/main/java/pro/verron/docxstamper/api/ITypeResolver.java b/src/main/java/pro/verron/docxstamper/api/ITypeResolver.java index 5057976e..45e87836 100644 --- a/src/main/java/pro/verron/docxstamper/api/ITypeResolver.java +++ b/src/main/java/pro/verron/docxstamper/api/ITypeResolver.java @@ -13,7 +13,7 @@ public interface ITypeResolver { * @param expressionResult the result of an expression. Only objects of classes this type resolver is registered for * within the TypeResolverRegistry are passed into this method. * @return an object of the DOCX4J api (usually of type {@link R} = "run - * of text" that will be put in the place of an + * of text") that will be put in the place of an * expression found in the .docx document. */ R resolve(WordprocessingMLPackage document, T expressionResult); diff --git a/src/main/java/pro/verron/docxstamper/api/Image.java b/src/main/java/pro/verron/docxstamper/api/Image.java index ae1243e5..494984c8 100644 --- a/src/main/java/pro/verron/docxstamper/api/Image.java +++ b/src/main/java/pro/verron/docxstamper/api/Image.java @@ -41,7 +41,7 @@ public Image(InputStream in, Integer maxWidth) throws IOException { /** *

Constructor for Image.

* - * @param imageBytes - content of the image as array of the bytes + * @param imageBytes - content of the image as an array of the bytes */ public Image(byte[] imageBytes) { this.imageBytes = imageBytes; @@ -49,7 +49,7 @@ public Image(byte[] imageBytes) { /** *

Constructor for Image.

* - * @param imageBytes - content of the image as array of the bytes + * @param imageBytes - content of the image as an array of the bytes * @param maxWidth - max width of the image in twip */ public Image(byte[] imageBytes, Integer maxWidth) { diff --git a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java index 6e787e1b..29ff14a9 100644 --- a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java +++ b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java @@ -76,7 +76,7 @@ private static Optional getComment( // then we check if the child we are looking for is ours else if (possibleComment != null && run.equals(contentElement)) foundChild = true; - // and then if we have an end of a comment we are good! + // and then, if we have an end of a comment, we are good! else if (possibleComment != null && foundChild && unwrap( contentElement) instanceof CommentRangeEnd) { try { diff --git a/src/main/java/pro/verron/docxstamper/core/DefaultParagraph.java b/src/main/java/pro/verron/docxstamper/core/DefaultParagraph.java index 4cda56e1..56558fd1 100644 --- a/src/main/java/pro/verron/docxstamper/core/DefaultParagraph.java +++ b/src/main/java/pro/verron/docxstamper/core/DefaultParagraph.java @@ -189,7 +189,7 @@ private List getAffectedRuns(int startIndex, int endIndex) { } /** - * Returns the list of runs that are aggregated. Depending on what modifications were done to the aggregated text + * Returns the list of runs that are aggregated. Depending on what modifications were done to the aggregated text, * this list may not return the same runs initially added to the aggregator. * * @return the list of aggregated runs. diff --git a/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java b/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java index 6ed5c4f1..26817587 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java +++ b/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java @@ -767,7 +767,7 @@ private static Arguments conditionalDisplayOfTableRowsTest() { expected); } - private static Arguments conditionalDisplayOfTablesBug32Test() { + private static Arguments conditionalDisplayOfTableBug32Test() { var context = new Contexts.Name("Homer"); var template = getResource(Path.of( "ConditionalDisplayOfTablesBug32Test.docx")); @@ -791,7 +791,7 @@ private static Arguments conditionalDisplayOfTablesBug32Test() { expected); } - private static Arguments conditionalDisplayOfTablesTest() { + private static Arguments conditionalDisplayOfTableTest() { var context = new Contexts.Name("Homer"); var template = getResource(Path.of("ConditionalDisplayOfTablesTest" + ".docx")); @@ -980,7 +980,7 @@ private static Arguments expressionWithSurroundingSpacesTest() { expected); } - private static Arguments expressionReplacementWithCommentsTest() { + private static Arguments expressionReplacementWithCommentTest() { var context = new Contexts.Name("Homer Simpson"); var template = getResource(Path.of( "ExpressionReplacementWithCommentsTest.docx")); From 5f214b9af5da73ed9ee0a35c62832b984c15439b Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Fri, 15 Mar 2024 08:36:24 +0100 Subject: [PATCH 040/134] Refactor variable name and update bug report template Refactored the second parameter name in the 'role' function from 'danCastellaneta' to a more generic name 'actor' for the purpose of clarity. This commit also corrects minor punctuation issues and clarity in the bug report template. --- .github/ISSUE_TEMPLATE/bug_report.md | 9 +++++---- .../verron/docxstamper/test/utils/context/Contexts.java | 8 ++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index b5533893..e2e32ee7 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -12,9 +12,10 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' + +1. Go to '…' +2. Click on '…' +3. Scroll down to '…' 4. See error **Expected behavior** @@ -31,4 +32,4 @@ If applicable, add screenshots to help explain your problem. **Additional context** Add any other context about the problem here. -Notably you can attach the file to stamp, if the problems comes from a file. +Notably you can attach the file to stamp if the problems come from a file. diff --git a/src/test/java/pro/verron/docxstamper/test/utils/context/Contexts.java b/src/test/java/pro/verron/docxstamper/test/utils/context/Contexts.java index 557edddc..fc4dc5f4 100644 --- a/src/test/java/pro/verron/docxstamper/test/utils/context/Contexts.java +++ b/src/test/java/pro/verron/docxstamper/test/utils/context/Contexts.java @@ -60,13 +60,13 @@ record Names(List names) {} /** *

role.

* - * @param character a {@link java.lang.String} object - * @param danCastellaneta a {@link java.lang.String} object + * @param character a {@link java.lang.String} object + * @param actor a {@link java.lang.String} object * @return a {@link pro.verron.docxstamper.test.utils.context.Contexts.Role} object * @since 1.6.6 */ - public static Role role(String character, String danCastellaneta) { - return new Role(character, danCastellaneta); + public static Role role(String character, String actor) { + return new Role(character, actor); } /** From b380d67bb8337c8dd2eed5b913d899e36a4ae9ab Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 16 Mar 2024 10:14:54 +0100 Subject: [PATCH 041/134] Refactor test package structure for simplicity This commit consolidates all test classes into the main test package for improved simplicity and efficiency. The updated structuring eliminates the need for unnecessary packages and simplifies the test module information. This makes the test directory easier to navigate and manage. --- src/test/java/module-info.java | 2 -- .../test/{utils/context => }/Contexts.java | 14 +++++++------- .../test/{resolver => }/CustomTypeResolver.java | 3 +-- .../verron}/docxstamper/test/DefaultTests.java | 13 +++---------- .../test/{utils => }/DocxCollector.java | 2 +- .../test/FailOnUnresolvedPlaceholderTest.java | 5 ++--- .../docxstamper/test/{utils => }/IOStreams.java | 2 +- .../verron}/docxstamper/test/MultiSectionTest.java | 5 ++--- .../verron}/docxstamper/test/MultiStampTest.java | 7 +++---- .../test/NullPointerResolutionTest.java | 7 ++----- .../test/{utils/context => }/NullishContext.java | 14 +++++++------- ...laceholderReplacementInHeaderAndFooterTest.java | 5 ++--- .../PlaceholderReplacementInTextBoxesTest.java | 5 ++--- .../test/RepeatDocPartBadPlaceholderTest.java | 9 ++++----- .../docxstamper/test/{utils => }/RunCollector.java | 2 +- .../test/{accessors => }/SimpleGetter.java | 2 +- .../verron/docxstamper/test/SpelInjectionTest.java | 4 +--- .../verron/docxstamper/test/StampTableTest.java | 4 +--- .../docxstamper/test/{utils => }/Stringifier.java | 2 +- .../test/{utils/context => }/SubContext.java | 2 +- .../test/{utils => }/TestDocxStamper.java | 2 +- .../test/{utils => }/ThrowingSupplier.java | 2 +- 22 files changed, 45 insertions(+), 68 deletions(-) rename src/test/java/pro/verron/docxstamper/test/{utils/context => }/Contexts.java (96%) rename src/test/java/pro/verron/docxstamper/test/{resolver => }/CustomTypeResolver.java (86%) rename src/test/java/{org/wickedsource => pro/verron}/docxstamper/test/DefaultTests.java (99%) rename src/test/java/pro/verron/docxstamper/test/{utils => }/DocxCollector.java (96%) rename src/test/java/{org/wickedsource => pro/verron}/docxstamper/test/FailOnUnresolvedPlaceholderTest.java (89%) rename src/test/java/pro/verron/docxstamper/test/{utils => }/IOStreams.java (97%) rename src/test/java/{org/wickedsource => pro/verron}/docxstamper/test/MultiSectionTest.java (84%) rename src/test/java/{org/wickedsource => pro/verron}/docxstamper/test/MultiStampTest.java (92%) rename src/test/java/{org/wickedsource => pro/verron}/docxstamper/test/NullPointerResolutionTest.java (78%) rename src/test/java/pro/verron/docxstamper/test/{utils/context => }/NullishContext.java (85%) rename src/test/java/{org/wickedsource => pro/verron}/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java (93%) rename src/test/java/{org/wickedsource => pro/verron}/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java (85%) rename src/test/java/{org/wickedsource => pro/verron}/docxstamper/test/RepeatDocPartBadPlaceholderTest.java (86%) rename src/test/java/pro/verron/docxstamper/test/{utils => }/RunCollector.java (93%) rename src/test/java/pro/verron/docxstamper/test/{accessors => }/SimpleGetter.java (97%) rename src/test/java/pro/verron/docxstamper/test/{utils => }/Stringifier.java (99%) rename src/test/java/pro/verron/docxstamper/test/{utils/context => }/SubContext.java (97%) rename src/test/java/pro/verron/docxstamper/test/{utils => }/TestDocxStamper.java (99%) rename src/test/java/pro/verron/docxstamper/test/{utils => }/ThrowingSupplier.java (92%) diff --git a/src/test/java/module-info.java b/src/test/java/module-info.java index cbc19d79..78ac15f7 100644 --- a/src/test/java/module-info.java +++ b/src/test/java/module-info.java @@ -15,8 +15,6 @@ requires jakarta.xml.bind; opens pro.verron.docxstamper.test; - opens pro.verron.docxstamper.test.utils.context; - opens org.wickedsource.docxstamper.test; // exports pro.verron.docxstamper.test; // exports pro.verron.docxstamper.test.commentProcessors; diff --git a/src/test/java/pro/verron/docxstamper/test/utils/context/Contexts.java b/src/test/java/pro/verron/docxstamper/test/Contexts.java similarity index 96% rename from src/test/java/pro/verron/docxstamper/test/utils/context/Contexts.java rename to src/test/java/pro/verron/docxstamper/test/Contexts.java index fc4dc5f4..c9b0b676 100644 --- a/src/test/java/pro/verron/docxstamper/test/utils/context/Contexts.java +++ b/src/test/java/pro/verron/docxstamper/test/Contexts.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test.utils.context; +package pro.verron.docxstamper.test; import pro.verron.docxstamper.api.Image; import pro.verron.docxstamper.api.StampTable; @@ -62,7 +62,7 @@ record Names(List names) {} * * @param character a {@link java.lang.String} object * @param actor a {@link java.lang.String} object - * @return a {@link pro.verron.docxstamper.test.utils.context.Contexts.Role} object + * @return a {@link Contexts.Role} object * @since 1.6.6 */ public static Role role(String character, String actor) { @@ -72,8 +72,8 @@ public static Role role(String character, String actor) { /** *

roles.

* - * @param roles a {@link pro.verron.docxstamper.test.utils.context.Contexts.Role} object - * @return a {@link pro.verron.docxstamper.test.utils.context.Contexts.Characters} object + * @param roles a {@link Contexts.Role} object + * @return a {@link Contexts.Characters} object * @since 1.6.6 */ public static Characters roles(Role... roles) { @@ -105,7 +105,7 @@ public static HashMap subDocPartContext() { /** *

schoolContext.

* - * @return a {@link pro.verron.docxstamper.test.utils.context.Contexts.SchoolContext} object + * @return a {@link Contexts.SchoolContext} object * @since 1.6.6 */ public static SchoolContext schoolContext() { @@ -177,7 +177,7 @@ public static Map coupleContext() { /** *

nowContext.

* - * @return a {@link pro.verron.docxstamper.test.utils.context.Contexts.DateContext} object + * @return a {@link Contexts.DateContext} object * @since 1.6.6 */ public static DateContext nowContext() { @@ -205,7 +205,7 @@ public static HashMap mapAndReflectiveContext() { /** *

nullishContext.

* - * @return a {@link pro.verron.docxstamper.test.utils.context.Contexts.NullishContext} object + * @return a {@link Contexts.NullishContext} object * @since 1.6.6 */ public static NullishContext nullishContext() { diff --git a/src/test/java/pro/verron/docxstamper/test/resolver/CustomTypeResolver.java b/src/test/java/pro/verron/docxstamper/test/CustomTypeResolver.java similarity index 86% rename from src/test/java/pro/verron/docxstamper/test/resolver/CustomTypeResolver.java rename to src/test/java/pro/verron/docxstamper/test/CustomTypeResolver.java index 8f064ce1..f07425e1 100644 --- a/src/test/java/pro/verron/docxstamper/test/resolver/CustomTypeResolver.java +++ b/src/test/java/pro/verron/docxstamper/test/CustomTypeResolver.java @@ -1,7 +1,6 @@ -package pro.verron.docxstamper.test.resolver; +package pro.verron.docxstamper.test; import pro.verron.docxstamper.api.StringResolver; -import pro.verron.docxstamper.test.utils.context.Contexts; /** *

CustomTypeResolver class.

diff --git a/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java b/src/test/java/pro/verron/docxstamper/test/DefaultTests.java similarity index 99% rename from src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java rename to src/test/java/pro/verron/docxstamper/test/DefaultTests.java index 26817587..bce22a19 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/DefaultTests.java +++ b/src/test/java/pro/verron/docxstamper/test/DefaultTests.java @@ -1,4 +1,4 @@ -package org.wickedsource.docxstamper.test; +package pro.verron.docxstamper.test; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; @@ -11,13 +11,6 @@ import pro.verron.docxstamper.preset.Configurations; import pro.verron.docxstamper.preset.EvaluationContextConfigurers; import pro.verron.docxstamper.preset.Resolvers; -import pro.verron.docxstamper.test.CustomCommentProcessor; -import pro.verron.docxstamper.test.Functions; -import pro.verron.docxstamper.test.ICustomCommentProcessor; -import pro.verron.docxstamper.test.accessors.SimpleGetter; -import pro.verron.docxstamper.test.resolver.CustomTypeResolver; -import pro.verron.docxstamper.test.utils.TestDocxStamper; -import pro.verron.docxstamper.test.utils.context.Contexts; import java.io.IOException; import java.io.InputStream; @@ -31,7 +24,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.junit.jupiter.params.provider.Arguments.of; -import static pro.verron.docxstamper.test.utils.context.Contexts.*; +import static pro.verron.docxstamper.test.Contexts.*; /** *

DefaultTests class.

@@ -767,7 +760,7 @@ private static Arguments conditionalDisplayOfTableRowsTest() { expected); } - private static Arguments conditionalDisplayOfTableBug32Test() { + private static Arguments conditionalDisplayOfTablesBug32Test() { var context = new Contexts.Name("Homer"); var template = getResource(Path.of( "ConditionalDisplayOfTablesBug32Test.docx")); diff --git a/src/test/java/pro/verron/docxstamper/test/utils/DocxCollector.java b/src/test/java/pro/verron/docxstamper/test/DocxCollector.java similarity index 96% rename from src/test/java/pro/verron/docxstamper/test/utils/DocxCollector.java rename to src/test/java/pro/verron/docxstamper/test/DocxCollector.java index a1f54882..26e69780 100644 --- a/src/test/java/pro/verron/docxstamper/test/utils/DocxCollector.java +++ b/src/test/java/pro/verron/docxstamper/test/DocxCollector.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test.utils; +package pro.verron.docxstamper.test; import org.docx4j.TraversalUtil; diff --git a/src/test/java/org/wickedsource/docxstamper/test/FailOnUnresolvedPlaceholderTest.java b/src/test/java/pro/verron/docxstamper/test/FailOnUnresolvedPlaceholderTest.java similarity index 89% rename from src/test/java/org/wickedsource/docxstamper/test/FailOnUnresolvedPlaceholderTest.java rename to src/test/java/pro/verron/docxstamper/test/FailOnUnresolvedPlaceholderTest.java index 6f22e36a..580272c6 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/FailOnUnresolvedPlaceholderTest.java +++ b/src/test/java/pro/verron/docxstamper/test/FailOnUnresolvedPlaceholderTest.java @@ -1,9 +1,8 @@ -package org.wickedsource.docxstamper.test; +package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; import pro.verron.docxstamper.api.DocxStamperException; import pro.verron.docxstamper.preset.Configurations; -import pro.verron.docxstamper.test.utils.TestDocxStamper; import java.io.IOException; import java.io.InputStream; @@ -11,7 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.wickedsource.docxstamper.test.DefaultTests.getResource; +import static pro.verron.docxstamper.test.DefaultTests.getResource; /** * @author Joseph Verron diff --git a/src/test/java/pro/verron/docxstamper/test/utils/IOStreams.java b/src/test/java/pro/verron/docxstamper/test/IOStreams.java similarity index 97% rename from src/test/java/pro/verron/docxstamper/test/utils/IOStreams.java rename to src/test/java/pro/verron/docxstamper/test/IOStreams.java index 671c3882..57e2a9ae 100644 --- a/src/test/java/pro/verron/docxstamper/test/utils/IOStreams.java +++ b/src/test/java/pro/verron/docxstamper/test/IOStreams.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test.utils; +package pro.verron.docxstamper.test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/test/java/org/wickedsource/docxstamper/test/MultiSectionTest.java b/src/test/java/pro/verron/docxstamper/test/MultiSectionTest.java similarity index 84% rename from src/test/java/org/wickedsource/docxstamper/test/MultiSectionTest.java rename to src/test/java/pro/verron/docxstamper/test/MultiSectionTest.java index 6ff1c5eb..f68bf39b 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/MultiSectionTest.java +++ b/src/test/java/pro/verron/docxstamper/test/MultiSectionTest.java @@ -1,13 +1,12 @@ -package org.wickedsource.docxstamper.test; +package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; import pro.verron.docxstamper.preset.Configurations; -import pro.verron.docxstamper.test.utils.TestDocxStamper; import java.nio.file.Path; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.wickedsource.docxstamper.test.DefaultTests.getResource; +import static pro.verron.docxstamper.test.DefaultTests.getResource; /** * @author Joseph Verron diff --git a/src/test/java/org/wickedsource/docxstamper/test/MultiStampTest.java b/src/test/java/pro/verron/docxstamper/test/MultiStampTest.java similarity index 92% rename from src/test/java/org/wickedsource/docxstamper/test/MultiStampTest.java rename to src/test/java/pro/verron/docxstamper/test/MultiStampTest.java index 344382af..c3d27dba 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/MultiStampTest.java +++ b/src/test/java/pro/verron/docxstamper/test/MultiStampTest.java @@ -1,14 +1,13 @@ -package org.wickedsource.docxstamper.test; +package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; import pro.verron.docxstamper.preset.Configurations; -import pro.verron.docxstamper.test.utils.TestDocxStamper; import java.nio.file.Path; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.wickedsource.docxstamper.test.DefaultTests.getResource; -import static pro.verron.docxstamper.test.utils.context.Contexts.names; +import static pro.verron.docxstamper.test.Contexts.names; +import static pro.verron.docxstamper.test.DefaultTests.getResource; /** * @author Joseph Verron diff --git a/src/test/java/org/wickedsource/docxstamper/test/NullPointerResolutionTest.java b/src/test/java/pro/verron/docxstamper/test/NullPointerResolutionTest.java similarity index 78% rename from src/test/java/org/wickedsource/docxstamper/test/NullPointerResolutionTest.java rename to src/test/java/pro/verron/docxstamper/test/NullPointerResolutionTest.java index bc4f5790..33170d4c 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/NullPointerResolutionTest.java +++ b/src/test/java/pro/verron/docxstamper/test/NullPointerResolutionTest.java @@ -1,18 +1,15 @@ -package org.wickedsource.docxstamper.test; +package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; import pro.verron.docxstamper.api.DocxStamperException; import pro.verron.docxstamper.preset.Configurations; -import pro.verron.docxstamper.test.utils.TestDocxStamper; -import pro.verron.docxstamper.test.utils.context.NullishContext; -import pro.verron.docxstamper.test.utils.context.SubContext; import java.io.IOException; import java.nio.file.Path; import java.util.List; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.wickedsource.docxstamper.test.DefaultTests.getResource; +import static pro.verron.docxstamper.test.DefaultTests.getResource; /** * @author Joseph Verron diff --git a/src/test/java/pro/verron/docxstamper/test/utils/context/NullishContext.java b/src/test/java/pro/verron/docxstamper/test/NullishContext.java similarity index 85% rename from src/test/java/pro/verron/docxstamper/test/utils/context/NullishContext.java rename to src/test/java/pro/verron/docxstamper/test/NullishContext.java index 6b70d5eb..41392314 100644 --- a/src/test/java/pro/verron/docxstamper/test/utils/context/NullishContext.java +++ b/src/test/java/pro/verron/docxstamper/test/NullishContext.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test.utils.context; +package pro.verron.docxstamper.test; import java.util.Objects; @@ -25,9 +25,9 @@ public NullishContext() { *

Constructor for NullishContext.

* * @param fullish_value a {@link java.lang.String} object - * @param fullish a {@link pro.verron.docxstamper.test.utils.context.SubContext} object + * @param fullish a {@link SubContext} object * @param nullish_value a {@link java.lang.String} object - * @param nullish a {@link pro.verron.docxstamper.test.utils.context.SubContext} object + * @param nullish a {@link SubContext} object */ public NullishContext( String fullish_value, @@ -62,7 +62,7 @@ public void setFullish_value(String fullish_value) { /** *

Getter for the field fullish.

* - * @return a {@link pro.verron.docxstamper.test.utils.context.SubContext} object + * @return a {@link SubContext} object */ public SubContext getFullish() { return fullish; @@ -71,7 +71,7 @@ public SubContext getFullish() { /** *

Setter for the field fullish.

* - * @param fullish a {@link pro.verron.docxstamper.test.utils.context.SubContext} object + * @param fullish a {@link SubContext} object */ public void setFullish(SubContext fullish) { this.fullish = fullish; @@ -98,7 +98,7 @@ public void setNullish_value(String nullish_value) { /** *

Getter for the field nullish.

* - * @return a {@link pro.verron.docxstamper.test.utils.context.SubContext} object + * @return a {@link SubContext} object */ public SubContext getNullish() { return nullish; @@ -107,7 +107,7 @@ public SubContext getNullish() { /** *

Setter for the field nullish.

* - * @param nullish a {@link pro.verron.docxstamper.test.utils.context.SubContext} object + * @param nullish a {@link SubContext} object */ public void setNullish(SubContext nullish) { this.nullish = nullish; diff --git a/src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java b/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java similarity index 93% rename from src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java rename to src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java index dd990533..2a48504b 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java +++ b/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java @@ -1,13 +1,12 @@ -package org.wickedsource.docxstamper.test; +package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; import pro.verron.docxstamper.preset.Configurations; -import pro.verron.docxstamper.test.utils.TestDocxStamper; import java.nio.file.Path; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.wickedsource.docxstamper.test.DefaultTests.getResource; +import static pro.verron.docxstamper.test.DefaultTests.getResource; /** * @author Joseph Verron diff --git a/src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java b/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java similarity index 85% rename from src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java rename to src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java index c08a6d58..dbde8344 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java +++ b/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java @@ -1,15 +1,14 @@ -package org.wickedsource.docxstamper.test; +package pro.verron.docxstamper.test; import org.docx4j.dml.wordprocessingDrawing.Anchor; import org.junit.jupiter.api.Test; import pro.verron.docxstamper.preset.Configurations; -import pro.verron.docxstamper.test.utils.TestDocxStamper; import java.nio.file.Path; import java.util.List; import static org.junit.jupiter.api.Assertions.assertIterableEquals; -import static org.wickedsource.docxstamper.test.DefaultTests.getResource; +import static pro.verron.docxstamper.test.DefaultTests.getResource; /** * @author Joseph Verron diff --git a/src/test/java/org/wickedsource/docxstamper/test/RepeatDocPartBadPlaceholderTest.java b/src/test/java/pro/verron/docxstamper/test/RepeatDocPartBadPlaceholderTest.java similarity index 86% rename from src/test/java/org/wickedsource/docxstamper/test/RepeatDocPartBadPlaceholderTest.java rename to src/test/java/pro/verron/docxstamper/test/RepeatDocPartBadPlaceholderTest.java index 0fa628e4..9e554d0c 100644 --- a/src/test/java/org/wickedsource/docxstamper/test/RepeatDocPartBadPlaceholderTest.java +++ b/src/test/java/pro/verron/docxstamper/test/RepeatDocPartBadPlaceholderTest.java @@ -1,4 +1,4 @@ -package org.wickedsource.docxstamper.test; +package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; @@ -6,9 +6,8 @@ import org.slf4j.LoggerFactory; import pro.verron.docxstamper.api.DocxStamperException; import pro.verron.docxstamper.preset.Configurations; -import pro.verron.docxstamper.test.utils.TestDocxStamper; -import pro.verron.docxstamper.test.utils.context.Contexts.Characters; -import pro.verron.docxstamper.test.utils.context.Contexts.Role; +import pro.verron.docxstamper.test.Contexts.Characters; +import pro.verron.docxstamper.test.Contexts.Role; import java.nio.file.Path; import java.util.Arrays; @@ -16,7 +15,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.wickedsource.docxstamper.test.DefaultTests.getResource; +import static pro.verron.docxstamper.test.DefaultTests.getResource; /** * @author Jenei Attila diff --git a/src/test/java/pro/verron/docxstamper/test/utils/RunCollector.java b/src/test/java/pro/verron/docxstamper/test/RunCollector.java similarity index 93% rename from src/test/java/pro/verron/docxstamper/test/utils/RunCollector.java rename to src/test/java/pro/verron/docxstamper/test/RunCollector.java index 46b72023..34843bb2 100644 --- a/src/test/java/pro/verron/docxstamper/test/utils/RunCollector.java +++ b/src/test/java/pro/verron/docxstamper/test/RunCollector.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test.utils; +package pro.verron.docxstamper.test; import org.docx4j.utils.TraversalUtilVisitor; import org.docx4j.wml.R; diff --git a/src/test/java/pro/verron/docxstamper/test/accessors/SimpleGetter.java b/src/test/java/pro/verron/docxstamper/test/SimpleGetter.java similarity index 97% rename from src/test/java/pro/verron/docxstamper/test/accessors/SimpleGetter.java rename to src/test/java/pro/verron/docxstamper/test/SimpleGetter.java index 0d171e12..6edd1efe 100644 --- a/src/test/java/pro/verron/docxstamper/test/accessors/SimpleGetter.java +++ b/src/test/java/pro/verron/docxstamper/test/SimpleGetter.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test.accessors; +package pro.verron.docxstamper.test; import org.springframework.expression.EvaluationContext; import org.springframework.expression.PropertyAccessor; diff --git a/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java b/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java index b68f9b28..cb66d5cb 100644 --- a/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java +++ b/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java @@ -3,15 +3,13 @@ import org.junit.jupiter.api.Test; import pro.verron.docxstamper.api.DocxStamperException; import pro.verron.docxstamper.preset.Configurations; -import pro.verron.docxstamper.test.utils.TestDocxStamper; -import pro.verron.docxstamper.test.utils.context.Contexts; import java.io.IOException; import java.nio.file.Path; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.wickedsource.docxstamper.test.DefaultTests.getResource; +import static pro.verron.docxstamper.test.DefaultTests.getResource; /** * @author Joseph Verron diff --git a/src/test/java/pro/verron/docxstamper/test/StampTableTest.java b/src/test/java/pro/verron/docxstamper/test/StampTableTest.java index e65eabdb..17513779 100644 --- a/src/test/java/pro/verron/docxstamper/test/StampTableTest.java +++ b/src/test/java/pro/verron/docxstamper/test/StampTableTest.java @@ -2,14 +2,12 @@ import org.junit.jupiter.api.Test; import pro.verron.docxstamper.preset.Configurations; -import pro.verron.docxstamper.test.utils.TestDocxStamper; -import pro.verron.docxstamper.test.utils.context.Contexts; import java.nio.file.Path; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.wickedsource.docxstamper.test.DefaultTests.getResource; +import static pro.verron.docxstamper.test.DefaultTests.getResource; /** * A test class that verifies that stampTable feature works correctly diff --git a/src/test/java/pro/verron/docxstamper/test/utils/Stringifier.java b/src/test/java/pro/verron/docxstamper/test/Stringifier.java similarity index 99% rename from src/test/java/pro/verron/docxstamper/test/utils/Stringifier.java rename to src/test/java/pro/verron/docxstamper/test/Stringifier.java index 4ad9a146..f5af68dd 100644 --- a/src/test/java/pro/verron/docxstamper/test/utils/Stringifier.java +++ b/src/test/java/pro/verron/docxstamper/test/Stringifier.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test.utils; +package pro.verron.docxstamper.test; import jakarta.xml.bind.JAXBElement; import org.docx4j.TextUtils; diff --git a/src/test/java/pro/verron/docxstamper/test/utils/context/SubContext.java b/src/test/java/pro/verron/docxstamper/test/SubContext.java similarity index 97% rename from src/test/java/pro/verron/docxstamper/test/utils/context/SubContext.java rename to src/test/java/pro/verron/docxstamper/test/SubContext.java index a89098c8..2cb8a960 100644 --- a/src/test/java/pro/verron/docxstamper/test/utils/context/SubContext.java +++ b/src/test/java/pro/verron/docxstamper/test/SubContext.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test.utils.context; +package pro.verron.docxstamper.test; import java.util.List; import java.util.Objects; diff --git a/src/test/java/pro/verron/docxstamper/test/utils/TestDocxStamper.java b/src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java similarity index 99% rename from src/test/java/pro/verron/docxstamper/test/utils/TestDocxStamper.java rename to src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java index ac3c18d9..01e5491e 100644 --- a/src/test/java/pro/verron/docxstamper/test/utils/TestDocxStamper.java +++ b/src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test.utils; +package pro.verron.docxstamper.test; import org.docx4j.TraversalUtil; import org.docx4j.openpackaging.exceptions.Docx4JException; diff --git a/src/test/java/pro/verron/docxstamper/test/utils/ThrowingSupplier.java b/src/test/java/pro/verron/docxstamper/test/ThrowingSupplier.java similarity index 92% rename from src/test/java/pro/verron/docxstamper/test/utils/ThrowingSupplier.java rename to src/test/java/pro/verron/docxstamper/test/ThrowingSupplier.java index 42453655..a3c22766 100644 --- a/src/test/java/pro/verron/docxstamper/test/utils/ThrowingSupplier.java +++ b/src/test/java/pro/verron/docxstamper/test/ThrowingSupplier.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test.utils; +package pro.verron.docxstamper.test; import java.util.function.Supplier; From 205792e5796f584ded0b2c62a02dab09cb92e4c7 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 16 Mar 2024 12:49:54 +0100 Subject: [PATCH 042/134] In these unit tests, the docx formatting representation has been updated and fixed. --- .../verron/docxstamper/test/DefaultTests.java | 437 +++++++++--------- .../docxstamper/test/MultiStampTest.java | 8 +- ...olderReplacementInHeaderAndFooterTest.java | 8 +- .../verron/docxstamper/test/Stringifier.java | 184 +++++--- 4 files changed, 336 insertions(+), 301 deletions(-) diff --git a/src/test/java/pro/verron/docxstamper/test/DefaultTests.java b/src/test/java/pro/verron/docxstamper/test/DefaultTests.java index bce22a19..f144af3b 100644 --- a/src/test/java/pro/verron/docxstamper/test/DefaultTests.java +++ b/src/test/java/pro/verron/docxstamper/test/DefaultTests.java @@ -76,8 +76,8 @@ private static Arguments repeatingRows() { role("Krusty the Clown", "Dan Castellaneta")), getResource(Path.of("RepeatTableRowTest.docx")), """ - ❬Repeating Table Rows❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ - ❬❬List of Simpsons characters❘b=true❭❘b=true,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Repeating Table Rows❘spacing={after=120,before=240}❭ + ❬❬List of Simpsons characters❘b=true❭❘b=true,spacing={after=120,before=240}❭ ❬❬Character name❘b=true❭❘b=true❭ ❬❬Voice ❘b=true❭❬Actor❘b=true❭❘b=true❭ Homer Simpson @@ -92,8 +92,8 @@ private static Arguments repeatingRows() { Hank Azaria Krusty the Clown Dan Castellaneta - - ❬There are ❬6❘lang=de-DE❭ characters in the above table.❘lang=de-DE,spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""); + + ❬There are ❬6❘lang=de-DE❭ characters in the above table.❘lang=de-DE,spacing={after=140,before=0}❭"""); } private static Arguments ternary() { @@ -182,28 +182,17 @@ private static Arguments repeatTableRowKeepsFormatTest() { } private static Arguments repeatParagraphTest() { - var context = new Contexts.Characters(List.of(new Contexts.Role( - "Homer Simpson", - "Dan Castellaneta"), - new Contexts.Role( - "Marge Simpson", - "Julie Kavner"), - new Contexts.Role( - "Bart Simpson", - "Nancy Cartwright"), - new Contexts.Role( - "Kent Brockman", - "Harry Shearer"), - new Contexts.Role( - "Disco Stu", - "Hank Azaria"), - new Contexts.Role( - "Krusty the Clown", - "Dan Castellaneta"))); + var context = new Contexts.Characters(List.of( + new Contexts.Role("Homer Simpson", "Dan Castellaneta"), + new Contexts.Role("Marge Simpson", "Julie Kavner"), + new Contexts.Role("Bart Simpson", "Nancy Cartwright"), + new Contexts.Role("Kent Brockman", "Harry Shearer"), + new Contexts.Role("Disco Stu", "Hank Azaria"), + new Contexts.Role("Krusty the Clown", "Dan Castellaneta"))); var template = getResource(Path.of("RepeatParagraphTest.docx")); var expected = """ - ❬Repeating Paragraphs❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ - ❬❬List of Simpsons characters❘b=true❭❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Repeating Paragraphs❘spacing={after=120,before=240}❭ + ❬❬List of Simpsons characters❘b=true❭❘spacing={after=120,before=240}❭ Homer Simpson Dan Castellaneta Marge Simpson @@ -216,8 +205,8 @@ private static Arguments repeatParagraphTest() { Hank Azaria Krusty the Clown Dan Castellaneta - - ❬There are ❬6❘lang=de-DE❭ characters.❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; + + ❬There are ❬6❘lang=de-DE❭ characters.❘spacing={after=140,before=0}❭"""; return arguments("repeatParagraphTest", Configurations.standard(), @@ -287,42 +276,38 @@ private static Arguments repeatDocPartWithImagesInSourceTestshouldReplicateImage private static Arguments repeatDocPartTest() { return of("repeatDocPartTest", Configurations.standard(), - new Characters(List.of(new Role("Homer Simpson", - "Dan Castellaneta"), - new Role("Marge Simpson", - "Julie Kavner"), - new Role("Bart Simpson", - "Nancy Cartwright"), - new Role("Kent Brockman", - "Harry Shearer"), - new Role("Disco Stu", "Hank Azaria"), - new Role("Krusty the Clown", - "Dan Castellaneta"))), + new Characters(List.of( + new Role("Homer Simpson", "Dan Castellaneta"), + new Role("Marge Simpson", "Julie Kavner"), + new Role("Bart Simpson", "Nancy Cartwright"), + new Role("Kent Brockman", "Harry Shearer"), + new Role("Disco Stu", "Hank Azaria"), + new Role("Krusty the Clown", "Dan Castellaneta"))), getResource(Path.of("RepeatDocPartTest.docx")), """ Repeating Doc Part - ❬❬List ❘b=true❭❬of❘b=true❭❬ Simpsons ❘b=true❭❬characters❘b=true❭❘b=true,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ - ❬Paragraph for test: Homer Simpson - Dan Castellaneta❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬❬List ❘b=true❭❬of❘b=true❭❬ Simpsons ❘b=true❭❬characters❘b=true❭❘b=true,spacing={after=120,before=240}❭ + ❬Paragraph for test: Homer Simpson - Dan Castellaneta❘spacing={after=120,before=240}❭ ❬Homer Simpson❘jc=center❭ ❬Dan Castellaneta❘jc=center❭ ❬ |BR|❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬Paragraph for test: Marge Simpson - Julie Kavner❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Paragraph for test: Marge Simpson - Julie Kavner❘spacing={after=120,before=240}❭ ❬Marge Simpson❘jc=center❭ ❬Julie Kavner❘jc=center❭ ❬ |BR|❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬Paragraph for test: Bart Simpson - Nancy Cartwright❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Paragraph for test: Bart Simpson - Nancy Cartwright❘spacing={after=120,before=240}❭ ❬Bart Simpson❘jc=center❭ ❬Nancy Cartwright❘jc=center❭ ❬ |BR|❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬Paragraph for test: Kent Brockman - Harry Shearer❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Paragraph for test: Kent Brockman - Harry Shearer❘spacing={after=120,before=240}❭ ❬Kent Brockman❘jc=center❭ ❬Harry Shearer❘jc=center❭ ❬ |BR|❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬Paragraph for test: Disco Stu - Hank Azaria❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Paragraph for test: Disco Stu - Hank Azaria❘spacing={after=120,before=240}❭ ❬Disco Stu❘jc=center❭ ❬Hank Azaria❘jc=center❭ ❬ |BR|❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬Paragraph for test: Krusty the Clown - Dan Castellaneta❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Paragraph for test: Krusty the Clown - Dan Castellaneta❘spacing={after=120,before=240}❭ ❬Krusty the Clown❘jc=center❭ ❬Dan Castellaneta❘jc=center❭ ❬ |BR|❘suppressAutoHyphens=xxx,widowControl=xxx❭ @@ -340,151 +325,151 @@ private static Arguments repeatDocPartNestingTest() { ❬❬South Park Primary School❘lang=null❭❘lang=null,suppressAutoHyphens=xxx,widowControl=xxx❭ ❬❬Grade No.❘b=true,lang=null❭❬0❘b=true,lang=null❭❬ ❘b=true,lang=null❭❬t❘lang=null❭here are 3 classes❘b=true,lang=null,suppressAutoHyphens=xxx,widowControl=xxx❭ ❬Class No.0 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ ❬Class No.1 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ ❬Class No.2 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ ❬❬Grade No.❘b=true,lang=null❭❬1❘b=true,lang=null❭❬ ❘b=true,lang=null❭❬t❘lang=null❭here are 3 classes❘b=true,lang=null,suppressAutoHyphens=xxx,widowControl=xxx❭ ❬Class No.0 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ ❬Class No.1 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ ❬Class No.2 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ ❬❬Grade No.❘b=true,lang=null❭❬2❘b=true,lang=null❭❬ ❘b=true,lang=null❭❬t❘lang=null❭here are 3 classes❘b=true,lang=null,suppressAutoHyphens=xxx,widowControl=xxx❭ ❬Class No.0 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ ❬Class No.1 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ ❬Class No.2 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ There are 3 grades."""); } @@ -671,16 +656,16 @@ private static Arguments conditionalDisplayOfParagraphsTest_processorExpressions var template = getResource(Path.of( "ConditionalDisplayOfParagraphsTest.docx")); var expected = """ - ❬Conditional Display of Paragraphs❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Conditional Display of Paragraphs❘spacing={after=120,before=240}❭ ❬❬This paragraph stays untouched.❘lang=de-DE❭❘lang=de-DE❭ - ❬❬This paragraph stays untouched.❘lang=de-DE❭❘lang=de-DE,spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭ + ❬❬This paragraph stays untouched.❘lang=de-DE❭❘lang=de-DE,spacing={after=140,before=0}❭ ❬❬Conditional Display of paragraphs also works in tables❘b=true❭❘b=true❭ This paragraph stays untouched. - + ❬❬Also works in nested tables❘b=true❭❘b=true❭ This paragraph stays untouched. - - ❬❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; + + ❬❘spacing={after=140,before=0}❭"""; return arguments( "conditionalDisplayOfParagraphsTest_processorExpressionsInCommentsAreResolved", @@ -745,14 +730,14 @@ private static Arguments conditionalDisplayOfTableRowsTest() { var template = getResource(Path.of("ConditionalDisplayOfTableRowsTest" + ".docx")); var expected = """ - ❬Conditional Display of Table Rows❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Conditional Display of Table Rows❘spacing={after=120,before=240}❭ ❬❬This paragraph stays untouched.❘lang=de-DE❭❘lang=de-DE❭ This row stays untouched. This row stays untouched. ❬❬Also works on nested Tables❘b=true❭❘b=true❭ This row stays untouched. - - ❬❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; + + ❬❘spacing={after=140,before=0}❭"""; return arguments("conditionalDisplayOfTableRowsTest", Configurations.standard(), context, @@ -760,24 +745,24 @@ private static Arguments conditionalDisplayOfTableRowsTest() { expected); } - private static Arguments conditionalDisplayOfTablesBug32Test() { + private static Arguments conditionalDisplayOfTableBug32Test() { var context = new Contexts.Name("Homer"); var template = getResource(Path.of( "ConditionalDisplayOfTablesBug32Test.docx")); var expected = """ - ❬Conditional Display of Tables❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Conditional Display of Tables❘spacing={after=120,before=240}❭ ❬This paragraph stays untouched.❘lang=de-DE❭ - + ❬This table stays untouched.❘widowControl=xxx❭ ❬❘widowControl=xxx❭ ❬❘widowControl=xxx❭ ❬❘widowControl=xxx❭ - + ❬❬Also works on nested tables❘b=true❭❘b=true,widowControl=xxx❭ ❬❘b=true,widowControl=xxx❭ - - ❬❬This paragraph stays untouched.❘lang=de-DE❭❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; - return arguments("conditionalDisplayOfTablesBug32Test", + + ❬❬This paragraph stays untouched.❘lang=de-DE❭❘spacing={after=140,before=0}❭"""; + return arguments("conditionalDisplayOfTableBug32Test", Configurations.standard(), context, template, @@ -789,18 +774,18 @@ private static Arguments conditionalDisplayOfTableTest() { var template = getResource(Path.of("ConditionalDisplayOfTablesTest" + ".docx")); var expected = """ - ❬Conditional Display of Tables❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Conditional Display of Tables❘spacing={after=120,before=240}❭ ❬❬This paragraph stays untouched.❘lang=de-DE❭❘lang=de-DE❭ - + This table stays untouched. - - - - + + + + ❬❬Also works on nested tables❘b=true❭❘b=true❭ ❬❘b=true❭ - - ❬❬This paragraph stays untouched.❘lang=de-DE❭❘lang=de-DE,spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; + + ❬❬This paragraph stays untouched.❘lang=de-DE❭❘lang=de-DE,spacing={after=140,before=0}❭"""; return arguments("conditionalDisplayOfTablesTest", Configurations.standard(), context, @@ -813,9 +798,9 @@ private static Arguments customEvaluationContextConfigurerTest_customEvaluationC var template = getResource(Path.of( "CustomEvaluationContextConfigurerTest.docx")); var expected = """ - ❬Custom EvaluationContextConfigurer Test❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Custom EvaluationContextConfigurer Test❘spacing={after=120,before=240}❭ ❬❬This paragraph stays untouched.❘lang=de-DE❭❘lang=de-DE❭ - ❬The variable foo has the value ❬bar❘lang=de-DE❭.❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; + ❬The variable foo has the value ❬bar❘lang=de-DE❭.❘spacing={after=140,before=0}❭"""; var config = Configurations.standard() .setEvaluationContextConfigurer( evalContext -> evalContext.addPropertyAccessor(new SimpleGetter( @@ -834,10 +819,10 @@ private static Arguments customExpressionFunctionTest() { var context = new Contexts.Name("Homer Simpson"); var template = getResource(Path.of("CustomExpressionFunction.docx")); var expected = """ - ❬Custom Expression Function❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Custom Expression Function❘spacing={after=120,before=240}❭ ❬❬This paragraph is untouched.❘lang=de-DE❭❘lang=de-DE❭ - ❬❬In this paragraph, a custom expression function is used to uppercase a String: |BR|❘lang=de-DE❭❬HOMER SIMPSON❘b=true,lang=de-DE❭❬.❘lang=de-DE❭❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭ - ❬❬To test that custom functions work together with comment expressions, we toggle visibility of this paragraph with a comment expression.❘lang=de-DE❭❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; + ❬❬In this paragraph, a custom expression function is used to uppercase a String: |BR|❘lang=de-DE❭❬HOMER SIMPSON❘b=true,lang=de-DE❭❬.❘lang=de-DE❭❘spacing={after=140,before=0}❭ + ❬❬To test that custom functions work together with comment expressions, we toggle visibility of this paragraph with a comment expression.❘lang=de-DE❭❘spacing={after=140,before=0}❭"""; var config = Configurations.standard() .exposeInterfaceToExpressionLanguage( Functions.UppercaseFunction.class, @@ -856,10 +841,10 @@ private static Arguments customTypeResolverTest() { new Context(new CustomType()), getResource(Path.of("CustomTypeResolverTest.docx")), """ - ❬Custom TypeResolver Test❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Custom TypeResolver Test❘spacing={after=120,before=240}❭ ❬This paragraph is untouched.❘lang=de-DE❭ ❬The name should be resolved to ❘lang=de-DE❭❬foo❘b=true,lang=de-DE❭❬.❘lang=de-DE❭ - ❬❬This paragraph is untouched.❘lang=de-DE❭❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""); + ❬❬This paragraph is untouched.❘lang=de-DE❭❘spacing={after=140,before=0}❭"""); } private static Arguments dateReplacementTest() { @@ -868,7 +853,7 @@ private static Arguments dateReplacementTest() { var defaultFormat = new SimpleDateFormat("dd.MM.yyyy"); var formattedDate = defaultFormat.format(context.date()); var expected = """ - ❬Replacing date expressions❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Replacing date expressions❘spacing={after=120,before=240}❭ Today is: %s""".formatted(formattedDate); return arguments("dateReplacementTest", @@ -883,10 +868,10 @@ private static Arguments expressionReplacementInGlobalParagraphsTest() { var template = getResource( Path.of("ExpressionReplacementInGlobalParagraphsTest.docx")); var expected = """ - ❬Expression Replacement in global paragraphs❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Expression Replacement in global paragraphs❘spacing={after=120,before=240}❭ ❬❬This paragraph is untouched.❘lang=de-DE❭❘lang=de-DE❭ ❬In this paragraph, the variable ❘lang=de-DE❭❬name❘b=true,lang=de-DE❭ should be resolved to the value ❬Homer Simpson❘lang=de-DE❭. - ❬❬In this paragraph, the variable ❘lang=de-DE❭❬foo❘b=true,lang=de-DE❭❬ should not be resolved: ${foo}.❘lang=de-DE❭❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; + ❬❬In this paragraph, the variable ❘lang=de-DE❭❬foo❘b=true,lang=de-DE❭❬ should not be resolved: ${foo}.❘lang=de-DE❭❘spacing={after=140,before=0}❭"""; DocxStamperConfiguration config = Configurations.standard() .setFailOnUnresolvedExpression(false); return arguments("expressionReplacementInGlobalParagraphsTest", @@ -902,7 +887,7 @@ private static Arguments expressionReplacementInTablesTest() { ".docx")); var expected = """ - ❬Expression Replacement in Tables❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Expression Replacement in Tables❘spacing={after=120,before=240}❭ This should resolve to a name: Bart Simpson This should not resolve: @@ -912,8 +897,8 @@ private static Arguments expressionReplacementInTablesTest() { Bart Simpson This should not resolve: ${foo} - - ❬❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; + + ❬❘spacing={after=140,before=0}❭"""; DocxStamperConfiguration config = Configurations.standard() .setFailOnUnresolvedExpression(false); return arguments("expressionReplacementInTablesTest", @@ -957,7 +942,7 @@ private static Arguments expressionWithSurroundingSpacesTest() { var template = getResource(Path.of( "ExpressionWithSurroundingSpacesTest.docx")); var expected = """ - ❬Expression Replacement when expression has leading and/or trailing spaces❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Expression Replacement when expression has leading and/or trailing spaces❘spacing={after=120,before=240}❭ When an expression within a paragraph is resolved, the spaces between the replacement and the surrounding text should be as expected. The following paragraphs should all look the same. Before Expression After. Before Expression After. @@ -965,7 +950,7 @@ private static Arguments expressionWithSurroundingSpacesTest() { Before Expression After. Before Expression After. Before Expression After. - ❬Before Expression After.❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; + ❬Before Expression After.❘spacing={after=140,before=0}❭"""; return arguments("expressionWithSurroundingSpacesTest", Configurations.standard(), spacyContext, @@ -978,10 +963,10 @@ private static Arguments expressionReplacementWithCommentTest() { var template = getResource(Path.of( "ExpressionReplacementWithCommentsTest.docx")); var expected = """ - ❬Expression Replacement with comments❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Expression Replacement with comments❘spacing={after=120,before=240}❭ This paragraph is untouched. In this paragraph, the variable ❬name❘b=true❭ should be resolved to the value Homer Simpson. - ❬In this paragraph, the variable ❬foo❘b=true❭ should not be resolved: unresolvedValueWithCommentreplaceWordWith(foo).❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; + ❬In this paragraph, the variable ❬foo❘b=true❭ should not be resolved: unresolvedValueWithCommentreplaceWordWith(foo).❘spacing={after=140,before=0,line=288,lineRule=AUTO}❭"""; var config = Configurations.standard() .setFailOnUnresolvedExpression(false); return arguments("expressionReplacementWithCommentsTest", @@ -1000,10 +985,10 @@ private static Arguments imageReplacementInGlobalParagraphsTest() { var template = getResource(Path.of( "ImageReplacementInGlobalParagraphsTest.docx")); var expected = """ - ❬Image Replacement in global paragraphs❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Image Replacement in global paragraphs❘spacing={after=120,before=240}❭ ❬❬This paragraph is untouched.❘lang=de-DE❭❘lang=de-DE❭ ❬In this paragraph, an image of Mona Lisa is inserted: ❬rId4:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:1276350❘lang=de-DE❭.❘lang=de-DE❭ - ❬This paragraph has the image ❬rId5:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:1276350❘lang=de-DE❭ in the middle.❘lang=de-DE,spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; + ❬This paragraph has the image ❬rId5:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:1276350❘lang=de-DE❭ in the middle.❘lang=de-DE,spacing={after=140,before=0}❭"""; return arguments("imageReplacementInGlobalParagraphsTest", Configurations.standard(), context, @@ -1018,10 +1003,10 @@ private static Arguments imageReplacementInGlobalParagraphsTestWithMaxWidth() { var template = getResource(Path.of( "ImageReplacementInGlobalParagraphsTest.docx")); var expected = """ - ❬Image Replacement in global paragraphs❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Image Replacement in global paragraphs❘spacing={after=120,before=240}❭ ❬❬This paragraph is untouched.❘lang=de-DE❭❘lang=de-DE❭ ❬In this paragraph, an image of Mona Lisa is inserted: ❬rId4:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:635000❘lang=de-DE❭.❘lang=de-DE❭ - ❬This paragraph has the image ❬rId5:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:635000❘lang=de-DE❭ in the middle.❘lang=de-DE,spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭"""; + ❬This paragraph has the image ❬rId5:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:635000❘lang=de-DE❭ in the middle.❘lang=de-DE,spacing={after=140,before=0}❭"""; return arguments("imageReplacementInGlobalParagraphsTestWithMaxWidth", Configurations.standard(), context, @@ -1217,8 +1202,8 @@ public static Stream tests() { conditionalDisplayOfParagraphsTest_inlineProcessorExpressionsAreResolved(), conditionalDisplayOfParagraphsTest_unresolvedInlineProcessorExpressionsAreRemoved(), conditionalDisplayOfTableRowsTest(), - conditionalDisplayOfTablesBug32Test(), - conditionalDisplayOfTablesTest(), + conditionalDisplayOfTableBug32Test(), + conditionalDisplayOfTableTest(), customEvaluationContextConfigurerTest_customEvaluationContextConfigurerIsHonored(), customExpressionFunctionTest(), customTypeResolverTest(), @@ -1227,7 +1212,7 @@ public static Stream tests() { expressionReplacementInTablesTest(), expressionReplacementWithFormattingTest(), expressionWithSurroundingSpacesTest(), - expressionReplacementWithCommentsTest(), + expressionReplacementWithCommentTest(), imageReplacementInGlobalParagraphsTest(), imageReplacementInGlobalParagraphsTestWithMaxWidth(), leaveEmptyOnExpressionErrorTest(), diff --git a/src/test/java/pro/verron/docxstamper/test/MultiStampTest.java b/src/test/java/pro/verron/docxstamper/test/MultiStampTest.java index c3d27dba..f64288a5 100644 --- a/src/test/java/pro/verron/docxstamper/test/MultiStampTest.java +++ b/src/test/java/pro/verron/docxstamper/test/MultiStampTest.java @@ -25,25 +25,25 @@ void expressionsAreResolvedOnMultiStamp() { var template1 = getResource(templatePath); var document1 = stamper.stampAndLoadAndExtract(template1, context); assertEquals(""" - ❬Multi-Stamp-Test❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Multi-Stamp-Test❘spacing={after=120,before=240}❭ This table row should be expanded to multiple rows each with a different name each time the document is stamped: Homer. This table row should be expanded to multiple rows each with a different name each time the document is stamped: Marge. This table row should be expanded to multiple rows each with a different name each time the document is stamped: Bart. This table row should be expanded to multiple rows each with a different name each time the document is stamped: Lisa. This table row should be expanded to multiple rows each with a different name each time the document is stamped: Maggie. - ❬❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭""", + ❬❘spacing={after=140,before=0}❭""", document1); var template2 = getResource(templatePath); var document2 = stamper.stampAndLoadAndExtract(template2, context); assertEquals(""" - ❬Multi-Stamp-Test❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ + ❬Multi-Stamp-Test❘spacing={after=120,before=240}❭ This table row should be expanded to multiple rows each with a different name each time the document is stamped: Homer. This table row should be expanded to multiple rows each with a different name each time the document is stamped: Marge. This table row should be expanded to multiple rows each with a different name each time the document is stamped: Bart. This table row should be expanded to multiple rows each with a different name each time the document is stamped: Lisa. This table row should be expanded to multiple rows each with a different name each time the document is stamped: Maggie. - ❬❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭""", + ❬❘spacing={after=140,before=0}❭""", document2); } } diff --git a/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java b/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java index 2a48504b..f49b3a13 100644 --- a/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java +++ b/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java @@ -25,12 +25,12 @@ void expressionReplacementInHeaderAndFooterTest() { assertEquals(""" ❬❬This ❘lang=de-DE❭❬header ❘lang=de-DE❭❬paragraph is untouched.❘lang=de-DE❭❘lang=de-DE❭ ❬❬In this paragraph, the variable ❘lang=de-DE❭❬name❘b=true,lang=de-DE❭ should be resolved to the value ❬Homer Simpson❘lang=de-DE❭.❘lang=de-DE❭ - ❬❬In this paragraph, the variable ❘lang=de-DE❭❬foo❘b=true,lang=de-DE❭❬ should not be resolved: ${foo}.❘lang=de-DE❭❘lang=de-DE,spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭ - ❬Expression Replacement in header and footer❘spacing={after=120,afterLines=120,before=120,beforeLines=120,line=120,lineRule=120}❭ - ❬❘spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭ + ❬❬In this paragraph, the variable ❘lang=de-DE❭❬foo❘b=true,lang=de-DE❭❬ should not be resolved: ${foo}.❘lang=de-DE❭❘lang=de-DE,spacing={after=140,before=0}❭ + ❬Expression Replacement in header and footer❘spacing={after=120,before=240}❭ + ❬❘spacing={after=140,before=0}❭ ❬❬This ❘lang=de-DE❭❬footer ❘lang=de-DE❭❬paragraph is untouched.❘lang=de-DE❭❘lang=de-DE❭ ❬❬In this paragraph, the variable ❘lang=de-DE❭❬name❘b=true,lang=de-DE❭ should be resolved to the value ❬Homer Simpson❘lang=de-DE❭.❘lang=de-DE❭ - ❬❬In this paragraph, the variable ❘lang=de-DE❭❬foo❘b=true,lang=de-DE❭❬ should not be resolved: ${foo}.❘lang=de-DE❭❘lang=de-DE,spacing={after=140,afterLines=140,before=140,beforeLines=140,line=140,lineRule=140}❭""", + ❬❬In this paragraph, the variable ❘lang=de-DE❭❬foo❘b=true,lang=de-DE❭❬ should not be resolved: ${foo}.❘lang=de-DE❭❘lang=de-DE,spacing={after=140,before=0}❭""", actual); } diff --git a/src/test/java/pro/verron/docxstamper/test/Stringifier.java b/src/test/java/pro/verron/docxstamper/test/Stringifier.java index f5af68dd..ad4303df 100644 --- a/src/test/java/pro/verron/docxstamper/test/Stringifier.java +++ b/src/test/java/pro/verron/docxstamper/test/Stringifier.java @@ -15,6 +15,7 @@ import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage; import org.docx4j.openpackaging.parts.WordprocessingML.CommentsPart; import org.docx4j.wml.*; +import org.docx4j.wml.Comments.Comment; import pro.verron.docxstamper.api.DocxStamperException; import java.math.BigInteger; @@ -23,6 +24,9 @@ import java.text.CharacterIterator; import java.text.StringCharacterIterator; import java.util.*; +import java.util.Map.Entry; +import java.util.function.Function; +import java.util.function.Predicate; import java.util.function.Supplier; import static java.util.Optional.ofNullable; @@ -42,7 +46,6 @@ public class Stringifier { /** *

Constructor for Stringifier.

* - * @param documentSupplier a {@link java.util.function.Supplier} object * @since 1.6.6 */ public Stringifier(Supplier documentSupplier) { @@ -65,7 +68,7 @@ private static MessageDigest findDigest() { * @return an Optional containing the Comment if found, or an empty Optional if not found * @throws Docx4JException if an error occurs while searching for the comment */ - public static Optional findComment( + public static Optional findComment( WordprocessingMLPackage document, BigInteger id ) throws Docx4JException { var name = new PartName("/word/comments.xml"); @@ -74,15 +77,50 @@ public static Optional findComment( var comments = wordComments.getContents(); return comments.getComment() .stream() - .filter(comment -> comment.getId() - .equals(id)) + .filter(idEqual(id)) .findFirst(); } + private static Predicate idEqual(BigInteger id) { + return comment -> { + var commentId = comment.getId(); + return commentId.equals(id); + }; + } + + private static void extract( + Map map, + String key, + Object value + ) { + if (value != null) + map.put(key, value); + } + + private static Function, String> format(String format) { + return entry -> format.formatted(entry.getKey(), entry.getValue()); + } + + private String stringify(Text text) { + return TextUtils.getText(text); + } + private WordprocessingMLPackage document() { return documentSupplier.get(); } + private String stringify(R.LastRenderedPageBreak lrpb) { + return ""; // do not render + } + + private String stringify(Br br) { + return "|BR|"; + } + + private String stringify(R.Tab tab) { + return "|TAB|"; + } + /** *

stringify.

* @@ -90,7 +128,7 @@ private WordprocessingMLPackage document() { * @return a {@link java.lang.String} object * @since 1.6.6 */ - public String stringify(CTBlip blip) { + private String stringify(CTBlip blip) { var image = document() .getParts() .getParts() @@ -99,7 +137,7 @@ public String stringify(CTBlip blip) { .filter(e -> e.getKey() .getName() .contains(blip.getEmbed())) - .map(Map.Entry::getValue) + .map(Entry::getValue) .findFirst() .map(BinaryPartAbstractImage.class::cast) .orElseThrow(); @@ -118,7 +156,7 @@ public String stringify(CTBlip blip) { * @return a {@link java.lang.String} object * @since 1.6.6 */ - public String humanReadableByteCountSI(long bytes) { + private String humanReadableByteCountSI(long bytes) { if (-1000 < bytes && bytes < 1000) return bytes + "B"; CharacterIterator ci = new StringCharacterIterator("kMGTPE"); @@ -145,50 +183,68 @@ private String sha1b64(byte[] imageBytes) { * @since 1.6.6 */ public String stringify(Object o) { - if (o instanceof JAXBElement jaxbElement) - return stringify(jaxbElement.getValue()); - if (o instanceof List list) - return list.stream() - .map(this::stringify) - .collect(joining()); - if (o instanceof Text text) - return TextUtils.getText(text); - if (o instanceof P p) - return stringify(p); - if (o instanceof Drawing drawing) - return stringify(drawing.getAnchorOrInline()); - if (o instanceof Inline inline) - return stringify(inline.getGraphic()) + ":" + inline.getExtent() - .getCx(); - if (o instanceof Graphic graphic) - return stringify(graphic.getGraphicData()); - if (o instanceof GraphicData graphicData) - return stringify(graphicData.getPic()); - if (o instanceof Pic pic) - return stringify(pic.getBlipFill()); - if (o instanceof CTBlipFillProperties blipFillProperties) - return stringify(blipFillProperties.getBlip()); - if (o instanceof CTBlip blip) - return stringify(blip); - if (o instanceof R.LastRenderedPageBreak) - return ""; // do not serialize - if (o instanceof Br) - return "|BR|"; - if (o instanceof R.Tab) - return "|TAB|"; - if (o instanceof R.CommentReference commentReference) { - try { - return findComment(document(), - commentReference.getId()) - .map(c -> stringify(c.getContent())) - .orElseThrow(); - } catch (Docx4JException e) { - throw new RuntimeException(e); - } + if (o instanceof JAXBElement jaxb) return stringify(jaxb.getValue()); + if (o instanceof List list) return stringify(list); + if (o instanceof Text text) return stringify(text); + if (o instanceof P p) return stringify(p); + if (o instanceof Drawing drawing) return stringify(drawing); + if (o instanceof Inline inline) return stringify(inline); + if (o instanceof Graphic graphic) return getStringify(graphic); + if (o instanceof GraphicData graphicData) return stringify(graphicData); + if (o instanceof Pic pic) return stringify(pic); + if (o instanceof CTBlipFillProperties bfp) return stringify(bfp); + if (o instanceof CTBlip blip) return stringify(blip); + if (o instanceof R.LastRenderedPageBreak lrpb) return stringify(lrpb); + if (o instanceof Br br) return stringify(br); + if (o instanceof R.Tab tab) return stringify(tab); + if (o instanceof R.CommentReference cr) return stringify(cr); + if (o == null) throw new RuntimeException("Unsupported content: NULL"); + throw new RuntimeException("Unsupported content: " + o.getClass()); + } + + private String stringify(Pic pic) { + return stringify(pic.getBlipFill()); + } + + private String stringify(CTBlipFillProperties blipFillProperties) { + return stringify(blipFillProperties.getBlip()); + } + + private String stringify(R.CommentReference commentReference) { + try { + return findComment(document(), + commentReference.getId()) + .map(c -> stringify(c.getContent())) + .orElseThrow(); + } catch (Docx4JException e) { + throw new RuntimeException(e); } - if (o == null) - throw new RuntimeException("Unsupported run content: NULL"); - throw new RuntimeException("Unsupported run content: " + o.getClass()); + } + + private String stringify(GraphicData graphicData) { + return stringify(graphicData.getPic()); + } + + private String getStringify(Graphic graphic) { + return stringify(graphic.getGraphicData()); + } + + private String stringify(Inline inline) { + var graphic = inline.getGraphic(); + var extent = inline.getExtent(); + return "%s:%d".formatted( + stringify(graphic), + extent.getCx()); + } + + private String stringify(Drawing drawing) { + return stringify(drawing.getAnchorOrInline()); + } + + private String stringify(List list) { + return list.stream() + .map(this::stringify) + .collect(joining()); } /** @@ -198,26 +254,20 @@ public String stringify(Object o) { * @return a {@link java.util.Optional} object * @since 1.6.6 */ - public Optional stringify(PPrBase.Spacing spacing) { + private Optional stringify(PPrBase.Spacing spacing) { if (spacing == null) return Optional.empty(); SortedMap map = new TreeMap<>(); - ofNullable(spacing.getAfter()) - .ifPresent(after -> map.put("after", after)); - ofNullable(spacing.getAfter()) - .ifPresent(after -> map.put("before", after)); - ofNullable(spacing.getAfter()) - .ifPresent(after -> map.put("beforeLines", after)); - ofNullable(spacing.getAfter()) - .ifPresent(after -> map.put("afterLines", after)); - ofNullable(spacing.getAfter()) - .ifPresent(after -> map.put("line", after)); - ofNullable(spacing.getAfter()) - .ifPresent(after -> map.put("lineRule", after)); + extract(map, "after", spacing.getAfter()); + extract(map, "before", spacing.getBefore()); + extract(map, "beforeLines", spacing.getBeforeLines()); + extract(map, "afterLines", spacing.getAfterLines()); + extract(map, "line", spacing.getLine()); + extract(map, "lineRule", spacing.getLineRule()); return map.isEmpty() ? Optional.empty() : Optional.of(map.entrySet() .stream() - .map(entry -> entry.getKey() + "=" + entry.getValue()) + .map(format("%s=%s")) .collect(joining(",", "{", "}"))); } @@ -228,7 +278,7 @@ public Optional stringify(PPrBase.Spacing spacing) { * @return a {@link java.lang.String} object * @since 1.6.6 */ - public String stringify(P p) { + private String stringify(P p) { String runs = extractDocumentRuns(p); return ofNullable(p.getPPr()) .flatMap(this::stringify) @@ -311,7 +361,7 @@ private Optional stringify(PPr pPr) { * @return a {@link java.lang.String} object * @since 1.6.6 */ - public String stringify(R run) { + private String stringify(R run) { String serialized = stringify(run.getContent()); if (serialized.isEmpty()) return ""; @@ -328,7 +378,7 @@ public String stringify(R run) { * @return a {@link java.lang.String} object * @since 1.6.6 */ - public Optional stringify(RPrAbstract rPr) { + private Optional stringify(RPrAbstract rPr) { if (rPr == null) return Optional.empty(); var set = new TreeSet(); From 346f0b3c2333d34df00fbb130caf2a8345bd258b Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 16 Mar 2024 13:15:34 +0100 Subject: [PATCH 043/134] Add maven-jar-plugin to pom.xml The maven-jar-plugin has been incorporated into the pom.xml file. This inclusion allows the addition of automatic module name to manifest entries, improving the project's module naming and identification capacity. --- pom.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pom.xml b/pom.xml index 5fa46ad3..01d7e379 100644 --- a/pom.xml +++ b/pom.xml @@ -144,6 +144,18 @@ UTF-8 + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + + pro.verron.docxstamper + + + + org.apache.maven.plugins maven-compiler-plugin From 8b989a2ade9117c0fc1cea7e6e037cbb37148f6d Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 16 Mar 2024 13:23:20 +0100 Subject: [PATCH 044/134] Add compatibility status link to README A link referring to compatibility status has been added to the README file. The name of the continuous integration workflow within the Github actions directory has been updated to reflect its focus on Docx4J. --- .github/workflows/integrate-docx4j.yml | 2 +- README.adoc | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/integrate-docx4j.yml b/.github/workflows/integrate-docx4j.yml index 76ddddf3..f962119c 100644 --- a/.github/workflows/integrate-docx4j.yml +++ b/.github/workflows/integrate-docx4j.yml @@ -3,7 +3,7 @@ # For more information see: # https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven -name: Continuous Integration for OS +name: Continuous Integration for Docx4J on: workflow_dispatch: diff --git a/README.adoc b/README.adoc index 3023e6de..c4e45d6f 100644 --- a/README.adoc +++ b/README.adoc @@ -213,6 +213,8 @@ Note that as of version 1.4.0, you have to provide the dependency to your versio This way, you can choose which version of Docx4J you want to use instead of having it dictated by docx-stamper. +Status of compatibility available here -> https://github.com/verronpro/docx-stamper/actions/workflows/integrate-docx4j.yml) + == Contribute If you have an issue or create a comment processor or type resolver that you think deserves to be part of the default distribution, feel free to open an issue or - even better - a pull request with your contribution. From b87e9e98d696d14c1eb310b275f431b82dc413b5 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 16 Mar 2024 17:49:28 +0100 Subject: [PATCH 045/134] Refactor code for modularization and visibility enhancements Several changes were made in preparation for an upcoming major version release. This includes opening exports and creating temporary classes to handle modularization and tightening of visibility rules. Also, CommentWrapper and ICommentProcessor were refactored to Comment and CommentProcessor respectively, and relevant imports were updated across several classes. The deprecated classes and methods are marked for removal in future releases. --- src/main/java/module-info.java | 21 ++-- .../wickedsource/docxstamper/DocxStamper.java | 22 ++-- .../docxstamper/DocxStamperConfiguration.java | 13 +- .../docxstamper/api/DocxStamperException.java | 9 +- .../api/EvaluationContextConfigurer.java | 5 + .../commentprocessor/ICommentProcessor.java | 9 +- .../api/preprocessor/PreProcessor.java | 7 +- .../preprocessor/MergeSameStyleRuns.java | 2 +- .../processor/CommentProcessorRegistry.java | 45 +++---- .../displayif/DisplayIfProcessor.java | 4 +- .../repeat/ParagraphRepeatProcessor.java | 18 +-- .../ParagraphResolverDocumentWalker.java | 4 +- .../repeat/RepeatDocPartProcessor.java | 26 ++-- .../processor/repeat/RepeatProcessor.java | 15 ++- .../ReplaceWithProcessor.java | 6 +- .../processor/table/StampTable.java | 28 +++++ .../processor/table/TableResolver.java | 6 +- .../replace/PlaceholderReplacer.java | 51 ++++++++ .../replace/typeresolver/image/Image.java | 6 + .../docxstamper/util/CommentUtil.java | 15 +++ .../docxstamper/util/CommentWrapper.java | 17 +++ .../verron/docxstamper/StamperFactory.java | 15 +++ .../api/AbstractCommentProcessor.java | 19 ++- .../api/{CommentWrapper.java => Comment.java} | 6 +- .../docxstamper/api/CommentProcessor.java | 4 +- .../docxstamper/api/DocxStamperException.java | 13 -- .../docxstamper/api/LoadingOpcStamper.java | 4 +- .../verron/docxstamper/api/OpcStamper.java | 8 +- ...tion.java => OpcStamperConfiguration.java} | 32 ++--- .../docxstamper/api/OpcStamperException.java | 13 ++ .../verron/docxstamper/core/CommentUtil.java | 112 +++++++++--------- .../docxstamper/core/PlaceholderReplacer.java | 8 +- .../{Expressions.java => Placeholders.java} | 20 ++-- ...mmentWrapper.java => StandardComment.java} | 62 +++++----- ...tParagraph.java => StandardParagraph.java} | 4 +- ...ceholder.java => StandardPlaceholder.java} | 2 +- ...sionFinder.java => PlaceholderFinder.java} | 6 +- .../preset/CommentProcessorFactory.java | 6 +- .../docxstamper/preset/Configurations.java | 9 -- .../preset/OpcStamperConfigurations.java | 18 +++ .../{Stampers.java => OpcStampers.java} | 25 ++-- .../preset/resolver/Resolvers.java | 13 ++ .../preset/resolver/StringResolver.java | 21 ++++ src/test/java/module-info.java | 9 +- .../test/CustomCommentProcessor.java | 4 +- .../verron/docxstamper/test/DefaultTests.java | 90 +++++++------- .../test/FailOnUnresolvedPlaceholderTest.java | 10 +- .../docxstamper/test/MultiSectionTest.java | 4 +- .../docxstamper/test/MultiStampTest.java | 4 +- .../test/NullPointerResolutionTest.java | 8 +- ...olderReplacementInHeaderAndFooterTest.java | 4 +- ...PlaceholderReplacementInTextBoxesTest.java | 4 +- .../test/RepeatDocPartBadPlaceholderTest.java | 8 +- .../docxstamper/test/SpelInjectionTest.java | 10 +- .../docxstamper/test/StampTableTest.java | 4 +- .../verron/docxstamper/test/Stringifier.java | 4 +- .../docxstamper/test/TestDocxStamper.java | 11 +- 57 files changed, 569 insertions(+), 354 deletions(-) create mode 100644 src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java create mode 100644 src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java create mode 100644 src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java create mode 100644 src/main/java/org/wickedsource/docxstamper/util/CommentWrapper.java create mode 100644 src/main/java/pro/verron/docxstamper/StamperFactory.java rename src/main/java/pro/verron/docxstamper/api/{CommentWrapper.java => Comment.java} (86%) delete mode 100644 src/main/java/pro/verron/docxstamper/api/DocxStamperException.java rename src/main/java/pro/verron/docxstamper/api/{DocxStamperConfiguration.java => OpcStamperConfiguration.java} (71%) create mode 100644 src/main/java/pro/verron/docxstamper/api/OpcStamperException.java rename src/main/java/pro/verron/docxstamper/core/{Expressions.java => Placeholders.java} (84%) rename src/main/java/pro/verron/docxstamper/core/{DefaultCommentWrapper.java => StandardComment.java} (90%) rename src/main/java/pro/verron/docxstamper/core/{DefaultParagraph.java => StandardParagraph.java} (99%) rename src/main/java/pro/verron/docxstamper/core/{DefaultPlaceholder.java => StandardPlaceholder.java} (94%) rename src/main/java/pro/verron/docxstamper/core/expression/{ExpressionFinder.java => PlaceholderFinder.java} (88%) delete mode 100644 src/main/java/pro/verron/docxstamper/preset/Configurations.java create mode 100644 src/main/java/pro/verron/docxstamper/preset/OpcStamperConfigurations.java rename src/main/java/pro/verron/docxstamper/preset/{Stampers.java => OpcStampers.java} (50%) create mode 100644 src/main/java/pro/verron/docxstamper/preset/resolver/Resolvers.java create mode 100644 src/main/java/pro/verron/docxstamper/preset/resolver/StringResolver.java diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index dc25f550..7b7c59ae 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,4 +1,4 @@ -module pro.verron.docxstamper { +module pro.verron.opcstamper { requires spring.core; requires spring.expression; @@ -14,11 +14,16 @@ exports pro.verron.docxstamper.api; exports pro.verron.docxstamper.preset; - // exports org.wickedsource.docxstamper; - // exports org.wickedsource.docxstamper.api; - // exports org.wickedsource.docxstamper.api.commentprocessor; - // exports org.wickedsource.docxstamper.el; - // exports org.wickedsource.docxstamper.util; - // exports org.wickedsource.docxstamper.processor; - // exports org.wickedsource.docxstamper.processor.table; + /** + * TODO: remove all the following exports in next version + */ + opens pro.verron.docxstamper; + exports pro.verron.docxstamper; + exports org.wickedsource.docxstamper; + exports org.wickedsource.docxstamper.api; + exports org.wickedsource.docxstamper.api.commentprocessor; + exports org.wickedsource.docxstamper.el; + exports org.wickedsource.docxstamper.util; + exports org.wickedsource.docxstamper.processor; + exports org.wickedsource.docxstamper.processor.table; } diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java index b0ed348b..8afd5582 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java @@ -11,10 +11,11 @@ import org.wickedsource.docxstamper.el.StandardMethodResolver; import org.wickedsource.docxstamper.processor.CommentProcessorRegistry; import pro.verron.docxstamper.api.*; -import pro.verron.docxstamper.core.Expressions; import pro.verron.docxstamper.core.ObjectResolverRegistry; import pro.verron.docxstamper.core.PlaceholderReplacer; -import pro.verron.docxstamper.preset.Stampers; +import pro.verron.docxstamper.core.Placeholders; +import pro.verron.docxstamper.preset.OpcStamperConfigurations; +import pro.verron.docxstamper.preset.OpcStampers; import java.io.InputStream; import java.io.OutputStream; @@ -31,7 +32,14 @@ * @author Joseph Verron * @version ${version} * @since 1.0.0 + * @deprecated since 1.6.8, This class has been deprecated in the effort + * of the library modularization, because it + * exposes too many implementation details to library users, and makes it + * hard to extend the library comfortably. + * It is recommended to use the {@link OpcStampers} methods instead. + * This class will not be exported in the future releases of the module. */ +@Deprecated(since = "1.6.8", forRemoval = true) public class DocxStamper implements OpcStamper { private final List preprocessors; @@ -41,11 +49,11 @@ public class DocxStamper /** * Creates a new DocxStamper with the default configuration. * - * @deprecated since 1.6.4, use {@link Stampers#newDocxStamper()} or {@link Stampers#nopreprocessingDocxStamper()} instead. + * @deprecated since 1.6.4, use {@link OpcStampers#docxStamper()} or {@link OpcStampers#nopreprocessingDocxStamper()} instead. */ @Deprecated(since = "1.6.4", forRemoval = true) public DocxStamper() { - this(new DocxStamperConfiguration()); + this(OpcStamperConfigurations.standard()); } /** @@ -53,7 +61,7 @@ public DocxStamper() { * * @param configuration the configuration to use for this DocxStamper. */ - public DocxStamper(pro.verron.docxstamper.api.DocxStamperConfiguration configuration) { + public DocxStamper(OpcStamperConfiguration configuration) { this( configuration.isFailOnUnresolvedExpression(), configuration.isReplaceUnresolvedExpressions(), @@ -112,7 +120,7 @@ private DocxStamper( replaceUnresolvedExpressions, unresolvedExpressionsDefaultValue, leaveEmptyOnExpressionError, - Expressions.raw(lineBreakPlaceholder) + Placeholders.raw(lineBreakPlaceholder) ); for (var entry : configurationCommentProcessors.entrySet()) { @@ -166,7 +174,7 @@ private static TypedValue throwException(ReflectiveOperationException exception) * *

* If you need a wider vocabulary of methods available in the comments, you can create your own ICommentProcessor - * and register it via {@link DocxStamperConfiguration#addCommentProcessor(Class, CommentProcessorBuilder)}. + * and register it via {@link OpcStamperConfiguration#addCommentProcessor(Class, Function)}. *

*/ public void stamp( diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java index b13ea7d9..ceba4877 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java @@ -15,6 +15,7 @@ import org.wickedsource.docxstamper.replace.typeresolver.TypeResolver; import pro.verron.docxstamper.api.*; import pro.verron.docxstamper.preset.CommentProcessorFactory; +import pro.verron.docxstamper.preset.OpcStamperConfigurations; import pro.verron.docxstamper.preset.Resolvers; import pro.verron.docxstamper.preset.resolver.Null2DefaultResolver; @@ -27,13 +28,22 @@ * The {@link DocxStamperConfiguration} class represents the configuration for * the {@link DocxStamper} class. * It provides methods to customize the behavior of the stamper. + * * @author Joseph Verron * @author Tom Hombergs * @version ${version} * @since 1.0.3 + * @deprecated since 1.6.8, This class has been deprecated in the effort + * of the library modularization, because it + * exposes too many implementation details to library users, and makes it + * hard to extend the library comfortably. + * It is recommended to use the {@link OpcStamperConfigurations#standard()} method and + * {@link OpcStamperConfiguration} interface instead. + * This class will not be exported in the future releases of the module. */ +@Deprecated(since = "1.6.8", forRemoval = true) public class DocxStamperConfiguration - implements pro.verron.docxstamper.api.DocxStamperConfiguration { + implements OpcStamperConfiguration { private final Map, Function> commentProcessors = new HashMap<>(); private final List resolvers = new ArrayList<>(); @@ -259,7 +269,6 @@ public DocxStamperConfiguration addCommentProcessor( /** * Creates a {@link DocxStamper} instance configured with this configuration. * - * @param a T class * @return a {@link DocxStamper} object * @deprecated use new {@link DocxStamper#DocxStamper(DocxStamperConfiguration)} instead */ diff --git a/src/main/java/org/wickedsource/docxstamper/api/DocxStamperException.java b/src/main/java/org/wickedsource/docxstamper/api/DocxStamperException.java index 996e6f90..37d6381c 100644 --- a/src/main/java/org/wickedsource/docxstamper/api/DocxStamperException.java +++ b/src/main/java/org/wickedsource/docxstamper/api/DocxStamperException.java @@ -1,5 +1,7 @@ package org.wickedsource.docxstamper.api; +import pro.verron.docxstamper.api.OpcStamperException; + /** * This class represents an exception that can be thrown during the processing of a Docx file using the DocxStamper library. * It extends the RuntimeException class and provides additional constructors to handle different scenarios. @@ -8,9 +10,14 @@ * @author Tom Hombergs * @version ${version} * @since 1.0.0 + * @deprecated since 1.6.8, This class has been deprecated in the effort + * of the library modularization. + * It is recommended to use the {@link OpcStamperException} class instead. + * This class will not be exported in the future releases of the module. */ +@Deprecated(since = "1.6.8", forRemoval = true) public class DocxStamperException - extends pro.verron.docxstamper.api.DocxStamperException { + extends OpcStamperException { /** *

Constructor for DocxStamperException.

diff --git a/src/main/java/org/wickedsource/docxstamper/api/EvaluationContextConfigurer.java b/src/main/java/org/wickedsource/docxstamper/api/EvaluationContextConfigurer.java index cc95b93e..a4893daf 100644 --- a/src/main/java/org/wickedsource/docxstamper/api/EvaluationContextConfigurer.java +++ b/src/main/java/org/wickedsource/docxstamper/api/EvaluationContextConfigurer.java @@ -12,7 +12,12 @@ * @author Mario Siegenthaler * @version ${version} * @since 1.0.13 + * @deprecated since 1.6.8, This class has been deprecated in the effort + * of the library modularization. + * It is recommended to use the {@link pro.verron.docxstamper.api.EvaluationContextConfigurer} class instead. + * This class will not be exported in the future releases of the module. */ +@Deprecated(since = "1.6.8", forRemoval = true) public interface EvaluationContextConfigurer extends pro.verron.docxstamper.api.EvaluationContextConfigurer { } diff --git a/src/main/java/org/wickedsource/docxstamper/api/commentprocessor/ICommentProcessor.java b/src/main/java/org/wickedsource/docxstamper/api/commentprocessor/ICommentProcessor.java index 9278ca27..7bf29185 100644 --- a/src/main/java/org/wickedsource/docxstamper/api/commentprocessor/ICommentProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/api/commentprocessor/ICommentProcessor.java @@ -1,5 +1,7 @@ package org.wickedsource.docxstamper.api.commentprocessor; +import pro.verron.docxstamper.api.CommentProcessor; + /** *

In a .docx template used by DocxStamper, you can comment paragraphs of text to manipulate them. The comments in * the .docx template are passed to an implementation of ICommentProcessor that understands the expression used @@ -29,7 +31,12 @@ * @author Tom Hombergs * @version ${version} * @since 1.0.0 + * @deprecated since 1.6.8, This class has been deprecated in the effort + * of the library modularization. + * It is recommended to use the {@link CommentProcessor} class instead. + * This class will not be exported in the future releases of the module. */ +@Deprecated(since = "1.6.8", forRemoval = true) public interface ICommentProcessor - extends pro.verron.docxstamper.api.CommentProcessor { + extends CommentProcessor { } diff --git a/src/main/java/org/wickedsource/docxstamper/api/preprocessor/PreProcessor.java b/src/main/java/org/wickedsource/docxstamper/api/preprocessor/PreProcessor.java index 3e5abfb3..79f7d32f 100644 --- a/src/main/java/org/wickedsource/docxstamper/api/preprocessor/PreProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/api/preprocessor/PreProcessor.java @@ -1,6 +1,5 @@ package org.wickedsource.docxstamper.api.preprocessor; -import org.wickedsource.docxstamper.DocxStamper; import org.wickedsource.docxstamper.preprocessor.MergeSameStyleRuns; import org.wickedsource.docxstamper.preprocessor.RemoveProofErrors; @@ -9,13 +8,17 @@ * document is processed by the DocxStamper. They can be used to manipulate the * document before the actual processing takes place. * - * @see DocxStamper * @see MergeSameStyleRuns * @see RemoveProofErrors * @author Joseph Verron * @version ${version} * @since 1.6.4 + * @deprecated since 1.6.8, This class has been deprecated in the effort + * of the library modularization. + * It is recommended to use the {@link pro.verron.docxstamper.api.PreProcessor} class instead. + * This class will not be exported in the future releases of the module. */ +@Deprecated(since = "1.6.8", forRemoval = true) public interface PreProcessor extends pro.verron.docxstamper.api.PreProcessor { } diff --git a/src/main/java/org/wickedsource/docxstamper/preprocessor/MergeSameStyleRuns.java b/src/main/java/org/wickedsource/docxstamper/preprocessor/MergeSameStyleRuns.java index c6cce16e..97c7322f 100644 --- a/src/main/java/org/wickedsource/docxstamper/preprocessor/MergeSameStyleRuns.java +++ b/src/main/java/org/wickedsource/docxstamper/preprocessor/MergeSameStyleRuns.java @@ -6,7 +6,7 @@ import org.docx4j.wml.ContentAccessor; import org.docx4j.wml.R; import org.docx4j.wml.RPr; -import org.wickedsource.docxstamper.api.preprocessor.PreProcessor; +import pro.verron.docxstamper.api.PreProcessor; import java.util.ArrayList; import java.util.LinkedHashSet; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java index b37f319c..96a82eca 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java @@ -15,10 +15,11 @@ import org.wickedsource.docxstamper.el.ExpressionResolver; import org.wickedsource.docxstamper.util.RunUtil; import org.wickedsource.docxstamper.util.walk.BaseCoordinatesWalker; -import pro.verron.docxstamper.api.CommentWrapper; +import pro.verron.docxstamper.api.Comment; +import pro.verron.docxstamper.api.CommentProcessor; import pro.verron.docxstamper.core.CommentUtil; -import pro.verron.docxstamper.core.DefaultParagraph; -import pro.verron.docxstamper.core.Expressions; +import pro.verron.docxstamper.core.Placeholders; +import pro.verron.docxstamper.core.StandardParagraph; import java.math.BigInteger; import java.util.ArrayList; @@ -79,7 +80,7 @@ public void runProcessors( T expressionContext ) { var comments = getComments(document); - var proceedComments = new ArrayList(); + var proceedComments = new ArrayList(); new BaseCoordinatesWalker() { @Override @@ -100,16 +101,16 @@ protected void onParagraph(P paragraph) { }.walk(document); for (Object processor : commentProcessors.values()) { - ((ICommentProcessor) processor).commitChanges(document); + ((CommentProcessor) processor).commitChanges(document); } - for (CommentWrapper commentWrapper : proceedComments) { - CommentUtil.deleteComment(commentWrapper); + for (Comment comment : proceedComments) { + CommentUtil.deleteComment(comment); } } - private Optional runProcessorsOnRunComment( + private Optional runProcessorsOnRunComment( WordprocessingMLPackage document, - Map comments, + Map comments, T expressionContext, P paragraph, R run @@ -131,9 +132,9 @@ private Optional runProcessorsOnRunComment( * @param paragraph the paragraph whose comments to evaluate. * @param the type of the context root object. */ - private Optional runProcessorsOnParagraphComment( + private Optional runProcessorsOnParagraphComment( WordprocessingMLPackage document, - Map comments, + Map comments, T expressionContext, P paragraph ) { @@ -157,13 +158,13 @@ private void runProcessorsOnInlineContent( T expressionContext, P paragraph ) { - var paragraphWrapper = new DefaultParagraph(paragraph); + var paragraphWrapper = new StandardParagraph(paragraph); String text = paragraphWrapper.asString(); - var expressions = Expressions.findProcessors(text); + var expressions = Placeholders.findProcessors(text); for (var expression : expressions) { for (final Object processor : commentProcessors.values()) { - ((ICommentProcessor) processor).setParagraph(paragraph); + ((CommentProcessor) processor).setParagraph(paragraph); } try { @@ -184,15 +185,15 @@ private void runProcessorsOnInlineContent( } } - private Optional runCommentProcessors( - Map comments, + private Optional runCommentProcessors( + Map comments, T expressionContext, @NonNull Comments.Comment comment, P paragraph, R run, WordprocessingMLPackage document ) { - CommentWrapper commentWrapper = comments.get(comment.getId()); + Comment commentWrapper = comments.get(comment.getId()); if (Objects.isNull(commentWrapper)) { // no comment to process @@ -202,11 +203,11 @@ private Optional runCommentProcessors( var commentExpression = getCommentString(comment); for (final Object processor : commentProcessors.values()) { - ((ICommentProcessor) processor).setParagraph(paragraph); - ((ICommentProcessor) processor).setCurrentRun(run); - ((ICommentProcessor) processor).setCurrentCommentWrapper( + ((CommentProcessor) processor).setParagraph(paragraph); + ((CommentProcessor) processor).setCurrentRun(run); + ((CommentProcessor) processor).setCurrentCommentWrapper( commentWrapper); - ((ICommentProcessor) processor).setDocument(document); + ((CommentProcessor) processor).setDocument(document); } try { @@ -236,7 +237,7 @@ private Optional runCommentProcessors( */ public void reset() { for (Object processor : commentProcessors.values()) { - ((ICommentProcessor) processor).reset(); + ((CommentProcessor) processor).reset(); } } } diff --git a/src/main/java/org/wickedsource/docxstamper/processor/displayif/DisplayIfProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/displayif/DisplayIfProcessor.java index 63dc4e58..f9c89838 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/displayif/DisplayIfProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/displayif/DisplayIfProcessor.java @@ -5,10 +5,10 @@ import org.docx4j.wml.Tbl; import org.docx4j.wml.Tc; import org.docx4j.wml.Tr; -import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.processor.CommentProcessingException; import org.wickedsource.docxstamper.util.ObjectDeleter; +import pro.verron.docxstamper.api.CommentProcessor; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.core.PlaceholderReplacer; @@ -39,7 +39,7 @@ private DisplayIfProcessor(ParagraphPlaceholderReplacer placeholderReplacer) { * @param pr the {@link PlaceholderReplacer} used for replacing expressions. * @return a new DisplayIfProcessor instance. */ - public static ICommentProcessor newInstance(ParagraphPlaceholderReplacer pr) { + public static CommentProcessor newInstance(ParagraphPlaceholderReplacer pr) { return new DisplayIfProcessor(pr); } diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java index e6b3aa2c..8d0a7166 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java @@ -4,15 +4,15 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.*; import org.wickedsource.docxstamper.api.DocxStamperException; -import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.util.ParagraphUtil; import org.wickedsource.docxstamper.util.SectionUtil; -import pro.verron.docxstamper.api.CommentWrapper; +import pro.verron.docxstamper.api.Comment; +import pro.verron.docxstamper.api.CommentProcessor; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.core.CommentUtil; -import pro.verron.docxstamper.core.DefaultParagraph; import pro.verron.docxstamper.core.PlaceholderReplacer; +import pro.verron.docxstamper.core.StandardParagraph; import java.math.BigInteger; import java.util.*; @@ -57,7 +57,7 @@ private ParagraphRepeatProcessor( * @return a new instance of ParagraphRepeatProcessor */ // TODO: remove ? - public static ICommentProcessor newInstance( + public static CommentProcessor newInstance( PlaceholderReplacer pr, String nullReplacement ) { @@ -72,7 +72,7 @@ public static ICommentProcessor newInstance( * @param placeholderReplacer replaces expressions with values * @return a new instance of ParagraphRepeatProcessor */ - public static ICommentProcessor newInstance(ParagraphPlaceholderReplacer placeholderReplacer) { + public static CommentProcessor newInstance(ParagraphPlaceholderReplacer placeholderReplacer) { return new ParagraphRepeatProcessor(placeholderReplacer, Collections::emptyList); } @@ -153,7 +153,7 @@ public void repeatParagraph(List objects) { Deque

paragraphs = getParagraphsInsideComment(paragraph); Paragraphs toRepeat = new Paragraphs(); - toRepeat.commentWrapper = getCurrentCommentWrapper(); + toRepeat.comment = getCurrentCommentWrapper(); toRepeat.data = new ArrayDeque<>(objects); toRepeat.paragraphs = paragraphs; toRepeat.sectionBreakBefore = SectionUtil.getPreviousSectionBreakIfPresent( @@ -198,10 +198,10 @@ private Deque

generateParagraphsToAdd( } CommentUtil.deleteCommentFromElement(pClone.getContent(), - paragraphs.commentWrapper.getComment() + paragraphs.comment.getComment() .getId()); placeholderReplacer.resolveExpressionsForParagraph( - new DefaultParagraph(pClone), + new StandardParagraph(pClone), expressionContext, document ); @@ -246,7 +246,7 @@ public void reset() { } private static class Paragraphs { - CommentWrapper commentWrapper; + Comment comment; Deque data; Deque

paragraphs; // hasOddSectionBreaks is true if the paragraphs to repeat contain an odd number of section breaks diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java index cb0587c1..75192002 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java @@ -5,7 +5,7 @@ import org.docx4j.wml.Tr; import org.wickedsource.docxstamper.util.walk.BaseDocumentWalker; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; -import pro.verron.docxstamper.core.DefaultParagraph; +import pro.verron.docxstamper.core.StandardParagraph; /** * Walks through a document and replaces expressions with values from the given @@ -48,7 +48,7 @@ public ParagraphResolverDocumentWalker( @Override protected void onParagraph(P paragraph) { placeholderReplacer.resolveExpressionsForParagraph( - new DefaultParagraph(paragraph), + new StandardParagraph(paragraph), expressionContext, document ); } diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java index 2bedb2a2..368ed6fa 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java @@ -6,12 +6,12 @@ import org.docx4j.wml.*; import org.jvnet.jaxb2_commons.ppp.Child; import org.wickedsource.docxstamper.api.DocxStamperException; -import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.util.DocumentUtil; import org.wickedsource.docxstamper.util.ParagraphUtil; import org.wickedsource.docxstamper.util.SectionUtil; -import pro.verron.docxstamper.api.CommentWrapper; +import pro.verron.docxstamper.api.Comment; +import pro.verron.docxstamper.api.CommentProcessor; import pro.verron.docxstamper.api.OpcStamper; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.core.PlaceholderReplacer; @@ -51,7 +51,7 @@ public class RepeatDocPartProcessor private static final ObjectFactory objectFactory = Context.getWmlObjectFactory(); private final OpcStamper stamper; - private final Map> contexts = new HashMap<>(); + private final Map> contexts = new HashMap<>(); private final Supplier> nullSupplier; private RepeatDocPartProcessor( @@ -73,7 +73,7 @@ private RepeatDocPartProcessor( * resolves to null * @return a new instance of this processor */ - public static ICommentProcessor newInstance( + public static CommentProcessor newInstance( PlaceholderReplacer pr, OpcStamper stamper, String nullReplacementValue @@ -90,7 +90,7 @@ public static ICommentProcessor newInstance( * @param stamper the stamper * @return a new instance of this processor */ - public static ICommentProcessor newInstance( + public static CommentProcessor newInstance( ParagraphPlaceholderReplacer pr, OpcStamper stamper ) { @@ -162,11 +162,11 @@ public void repeatDocPart(List contexts) { if (contexts == null) contexts = Collections.emptyList(); - CommentWrapper currentCommentWrapper = getCurrentCommentWrapper(); - List repeatElements = currentCommentWrapper.getRepeatElements(); + Comment currentComment = getCurrentCommentWrapper(); + List repeatElements = currentComment.getRepeatElements(); if (!repeatElements.isEmpty()) { - this.contexts.put(currentCommentWrapper, contexts); + this.contexts.put(currentComment, contexts); } } @@ -226,13 +226,13 @@ private Deque stampSubDocuments( */ @Override public void commitChanges(WordprocessingMLPackage document) { - for (Entry> entry : this.contexts.entrySet()) { - CommentWrapper commentWrapper = entry.getKey(); + for (Entry> entry : this.contexts.entrySet()) { + Comment comment = entry.getKey(); List expressionContexts = entry.getValue(); ContentAccessor gcp = Objects.requireNonNull( - commentWrapper.getParent()); - List repeatElements = commentWrapper.getRepeatElements(); - WordprocessingMLPackage subTemplate = commentWrapper.tryBuildingSubtemplate( + comment.getParent()); + List repeatElements = comment.getRepeatElements(); + WordprocessingMLPackage subTemplate = comment.tryBuildingSubtemplate( document); SectPr previousSectionBreak = SectionUtil.getPreviousSectionBreakIfPresent( repeatElements.get(0), gcp); diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java index 2ee02fbd..3b298389 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java @@ -3,10 +3,10 @@ import org.docx4j.XmlUtils; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.*; -import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.processor.CommentProcessingException; -import pro.verron.docxstamper.api.CommentWrapper; +import pro.verron.docxstamper.api.Comment; +import pro.verron.docxstamper.api.CommentProcessor; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.core.CommentUtil; import pro.verron.docxstamper.core.PlaceholderReplacer; @@ -29,7 +29,7 @@ public class RepeatProcessor extends BaseCommentProcessor implements IRepeatProc private final BiFunction> nullSupplier; private Map> tableRowsToRepeat = new HashMap<>(); - private Map tableRowsCommentsToRemove = new HashMap<>(); + private Map tableRowsCommentsToRemove = new HashMap<>(); private RepeatProcessor( ParagraphPlaceholderReplacer placeholderReplacer, @@ -45,7 +45,9 @@ private RepeatProcessor( * @param pr The PlaceholderReplacer to use. * @return A new RepeatProcessor. */ - public static ICommentProcessor newInstanceWithNullReplacement(PlaceholderReplacer pr) { + public static CommentProcessor newInstanceWithNullReplacement( + PlaceholderReplacer pr + ) { return new RepeatProcessor(pr, (document, row) -> RepeatProcessor.stampEmptyContext(pr, document, row)); } @@ -70,7 +72,7 @@ public static List stampEmptyContext(PlaceholderReplacer pr, WordprocessingM * @param pr The PlaceholderReplacer to use. * @return A new RepeatProcessor. */ - public static ICommentProcessor newInstance(ParagraphPlaceholderReplacer pr) { + public static CommentProcessor newInstance(ParagraphPlaceholderReplacer pr) { return new RepeatProcessor(pr, (document, row) -> emptyList()); } @@ -103,7 +105,8 @@ private void repeatRows(final WordprocessingMLPackage document) { changes = new ArrayList<>(); for (Object expressionContext : expressionContexts) { Tr rowClone = XmlUtils.deepCopy(row); - CommentWrapper commentWrapper = Objects.requireNonNull(tableRowsCommentsToRemove.get(row)); + Comment commentWrapper = Objects.requireNonNull( + tableRowsCommentsToRemove.get(row)); Comments.Comment comment = Objects.requireNonNull(commentWrapper.getComment()); BigInteger commentId = comment.getId(); CommentUtil.deleteCommentFromElement(rowClone.getContent(), commentId); diff --git a/src/main/java/org/wickedsource/docxstamper/processor/replaceExpression/ReplaceWithProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/replaceExpression/ReplaceWithProcessor.java index e80126bd..f3f3d8ac 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/replaceExpression/ReplaceWithProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/replaceExpression/ReplaceWithProcessor.java @@ -3,9 +3,9 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; import org.wickedsource.docxstamper.api.DocxStamperException; -import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.util.RunUtil; +import pro.verron.docxstamper.api.CommentProcessor; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.core.PlaceholderReplacer; @@ -44,7 +44,7 @@ private ReplaceWithProcessor( * @param nullReplacementValue a {@link String} object * @return the processor */ - public static ICommentProcessor newInstance( + public static CommentProcessor newInstance( PlaceholderReplacer pr, String nullReplacementValue ) { @@ -59,7 +59,7 @@ public static ICommentProcessor newInstance( * @param pr the placeholder replacer to use * @return the processor */ - public static ICommentProcessor newInstance(ParagraphPlaceholderReplacer pr) { + public static CommentProcessor newInstance(ParagraphPlaceholderReplacer pr) { return new ReplaceWithProcessor(pr, R::getContent); } diff --git a/src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java b/src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java new file mode 100644 index 00000000..6b442464 --- /dev/null +++ b/src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java @@ -0,0 +1,28 @@ +package org.wickedsource.docxstamper.processor.table; + +import org.springframework.lang.NonNull; + +import java.util.List; + +/** + * @deprecated since 1.6.8, This class has been deprecated in the effort + * of the library modularization. + * It is recommended to use the + * {@link pro.verron.docxstamper.api.StampTable} class instead. + * This class will be removed in the future releases of the module. + */ +@Deprecated(since = "1.6.8", forRemoval = true) +public class StampTable + extends pro.verron.docxstamper.api.StampTable { + public StampTable() { + super(); + } + + public StampTable( + @NonNull List headers, + @NonNull List> records + ) { + super(headers, records); + } + +} diff --git a/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java b/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java index f5bab6cc..79dfbf95 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java @@ -4,10 +4,10 @@ import org.docx4j.XmlUtils; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.*; -import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.processor.CommentProcessingException; import org.wickedsource.docxstamper.util.ParagraphUtil; +import pro.verron.docxstamper.api.CommentProcessor; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.api.StampTable; import pro.verron.docxstamper.core.PlaceholderReplacer; @@ -46,7 +46,7 @@ private TableResolver( * @param nullReplacementValue in case the value to interpret is null * @return a new {@link TableResolver} instance */ - public static ICommentProcessor newInstance( + public static CommentProcessor newInstance( PlaceholderReplacer pr, String nullReplacementValue ) { @@ -61,7 +61,7 @@ public static ICommentProcessor newInstance( * @param pr a {@link PlaceholderReplacer} instance * @return a new {@link TableResolver} instance */ - public static ICommentProcessor newInstance(ParagraphPlaceholderReplacer pr) { + public static CommentProcessor newInstance(ParagraphPlaceholderReplacer pr) { return new TableResolver(pr, table -> Collections.emptyList()); } diff --git a/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java b/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java new file mode 100644 index 00000000..dc518ab0 --- /dev/null +++ b/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java @@ -0,0 +1,51 @@ +package org.wickedsource.docxstamper.replace; + +import org.wickedsource.docxstamper.el.ExpressionResolver; +import pro.verron.docxstamper.api.Placeholder; +import pro.verron.docxstamper.core.ObjectResolverRegistry; + +/** + * @deprecated since 1.6.8, This class has been deprecated in the effort + * of the library modularization. + * It is recommended to use the {@link pro.verron.docxstamper.core.PlaceholderReplacer} class instead. + * This class will not be exported in the future releases of the module. + */ +@Deprecated(since = "1.6.8", forRemoval = true) +public class PlaceholderReplacer + extends pro.verron.docxstamper.core.PlaceholderReplacer { + /** + *

Constructor for PlaceholderReplacer.

+ * + * @param registry the registry containing all available type resolvers. + * @param resolver the expression resolver used to resolve expressions in the document. + * @param failOnUnresolvedExpression if set to true, an exception is thrown when an expression cannot be + * resolved. + * @param replaceUnresolvedExpressions if set to true, expressions that cannot be resolved are replaced by the + * value provided in the unresolvedExpressionsDefaultValue parameter. + * @param unresolvedExpressionsDefaultValue the value to use when replacing unresolved expressions. + * @param leaveEmptyOnExpressionError if set to true, expressions + * that cannot be resolved will + * be by replaced by an + * empty string. + * @param linebreakPlaceholder if set to a non-null value, + * all occurrences of this placeholder will be + * replaced with a line break. + */ + public PlaceholderReplacer( + ObjectResolverRegistry registry, + ExpressionResolver resolver, + boolean failOnUnresolvedExpression, + boolean replaceUnresolvedExpressions, + String unresolvedExpressionsDefaultValue, + boolean leaveEmptyOnExpressionError, + Placeholder linebreakPlaceholder + ) { + super(registry, + resolver, + failOnUnresolvedExpression, + replaceUnresolvedExpressions, + unresolvedExpressionsDefaultValue, + leaveEmptyOnExpressionError, + linebreakPlaceholder); + } +} diff --git a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java index 828ff5ca..8c4e02bf 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java @@ -10,6 +10,12 @@ * @author Romster * @version ${version} * @since 1.0.0 + * + * @deprecated since 1.6.8, This class has been deprecated in the effort + * of the library modularization. + * It is recommended to use the + * {@link pro.verron.docxstamper.api.Image} class instead. + * This class will not be exported in the future releases of the module. */ @Deprecated(since = "1.6.8", forRemoval = true) public final class Image diff --git a/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java b/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java new file mode 100644 index 00000000..3ca3c152 --- /dev/null +++ b/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java @@ -0,0 +1,15 @@ +package org.wickedsource.docxstamper.util; + +/** + * @deprecated since 1.6.8, This class has been deprecated in the effort + * of the library modularization. + * It is recommended to use the + * {@link pro.verron.docxstamper.core.CommentUtil} class instead. + * This class will be removed in the future releases of the module. + */ +@Deprecated(since = "1.6.8", forRemoval = true) +public class CommentUtil + extends pro.verron.docxstamper.core.CommentUtil { + + private CommentUtil() {} +} diff --git a/src/main/java/org/wickedsource/docxstamper/util/CommentWrapper.java b/src/main/java/org/wickedsource/docxstamper/util/CommentWrapper.java new file mode 100644 index 00000000..70cade76 --- /dev/null +++ b/src/main/java/org/wickedsource/docxstamper/util/CommentWrapper.java @@ -0,0 +1,17 @@ +package org.wickedsource.docxstamper.util; + +import pro.verron.docxstamper.core.StandardComment; + +/** + * @deprecated since 1.6.8, This class has been deprecated in the effort + * of the library modularization. + * It is recommended to use the + * {@link pro.verron.docxstamper.core.StandardComment} class and + * {@link pro.verron.docxstamper.api.Comment} interface + * instead. + * This class will be moved to internals in the future releases of the module. + */ +@Deprecated(since = "1.6.8", forRemoval = true) +public class CommentWrapper + extends StandardComment { +} diff --git a/src/main/java/pro/verron/docxstamper/StamperFactory.java b/src/main/java/pro/verron/docxstamper/StamperFactory.java new file mode 100644 index 00000000..e4a1bf9d --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/StamperFactory.java @@ -0,0 +1,15 @@ +package pro.verron.docxstamper; + +import pro.verron.docxstamper.preset.OpcStampers; + +/** + * @deprecated since 1.6.8, This class has been deprecated in the effort + * of the library modularization. + * It is recommended to use the + * {@link pro.verron.docxstamper.core.CommentUtil} class instead. + * This class will be removed in the future releases of the module. + */ +@Deprecated(since = "1.6.8", forRemoval = true) +public class StamperFactory + extends OpcStampers { +} diff --git a/src/main/java/pro/verron/docxstamper/api/AbstractCommentProcessor.java b/src/main/java/pro/verron/docxstamper/api/AbstractCommentProcessor.java index ce02cce4..db2f3a20 100644 --- a/src/main/java/pro/verron/docxstamper/api/AbstractCommentProcessor.java +++ b/src/main/java/pro/verron/docxstamper/api/AbstractCommentProcessor.java @@ -3,19 +3,18 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.P; import org.docx4j.wml.R; -import org.wickedsource.docxstamper.api.commentprocessor.ICommentProcessor; import java.util.Objects; public abstract class AbstractCommentProcessor - implements ICommentProcessor { + implements CommentProcessor { /** * PlaceholderReplacer used to replace expressions in the comment text. */ protected final ParagraphPlaceholderReplacer placeholderReplacer; private P paragraph; private R currentRun; - private CommentWrapper currentCommentWrapper; + private Comment currentComment; private WordprocessingMLPackage document; public AbstractCommentProcessor(ParagraphPlaceholderReplacer placeholderReplacer) {this.placeholderReplacer = placeholderReplacer;} @@ -23,20 +22,20 @@ public abstract class AbstractCommentProcessor /** *

Getter for the field currentCommentWrapper.

* - * @return a {@link CommentWrapper} object + * @return a {@link Comment} object */ - public CommentWrapper getCurrentCommentWrapper() { - return currentCommentWrapper; + public Comment getCurrentCommentWrapper() { + return currentComment; } /** * {@inheritDoc} */ @Override - public void setCurrentCommentWrapper(CommentWrapper currentCommentWrapper) { - Objects.requireNonNull(currentCommentWrapper.getCommentRangeStart()); - Objects.requireNonNull(currentCommentWrapper.getCommentRangeEnd()); - this.currentCommentWrapper = currentCommentWrapper; + public void setCurrentCommentWrapper(Comment currentComment) { + Objects.requireNonNull(currentComment.getCommentRangeStart()); + Objects.requireNonNull(currentComment.getCommentRangeEnd()); + this.currentComment = currentComment; } /** diff --git a/src/main/java/pro/verron/docxstamper/api/CommentWrapper.java b/src/main/java/pro/verron/docxstamper/api/Comment.java similarity index 86% rename from src/main/java/pro/verron/docxstamper/api/CommentWrapper.java rename to src/main/java/pro/verron/docxstamper/api/Comment.java index 69e22633..ce2d825c 100644 --- a/src/main/java/pro/verron/docxstamper/api/CommentWrapper.java +++ b/src/main/java/pro/verron/docxstamper/api/Comment.java @@ -6,7 +6,7 @@ import java.util.List; import java.util.Set; -public interface CommentWrapper { +public interface Comment { ContentAccessor getParent(); List getRepeatElements(); @@ -27,9 +27,9 @@ public interface CommentWrapper { void setCommentReference(R.CommentReference commentReference); - Set getChildren(); + Set getChildren(); - void setChildren(Set commentWrappers); + void setChildren(Set comments); Comments.Comment getComment(); diff --git a/src/main/java/pro/verron/docxstamper/api/CommentProcessor.java b/src/main/java/pro/verron/docxstamper/api/CommentProcessor.java index 9013a63f..0b44dc39 100644 --- a/src/main/java/pro/verron/docxstamper/api/CommentProcessor.java +++ b/src/main/java/pro/verron/docxstamper/api/CommentProcessor.java @@ -41,9 +41,9 @@ public interface CommentProcessor { * This method is always called BEFORE the custom methods of the custom comment * processor interface are called. * - * @param commentWrapper of the currently processed comment within the template. + * @param comment of the currently processed comment within the template. */ - void setCurrentCommentWrapper(CommentWrapper commentWrapper); + void setCurrentCommentWrapper(Comment comment); /** * Passes the processed document, to make all linked data diff --git a/src/main/java/pro/verron/docxstamper/api/DocxStamperException.java b/src/main/java/pro/verron/docxstamper/api/DocxStamperException.java deleted file mode 100644 index ff4b7589..00000000 --- a/src/main/java/pro/verron/docxstamper/api/DocxStamperException.java +++ /dev/null @@ -1,13 +0,0 @@ -package pro.verron.docxstamper.api; - -public class DocxStamperException - extends RuntimeException { - public DocxStamperException(String message) {super(message);} - - public DocxStamperException(Throwable cause) {super(cause);} - - public DocxStamperException(String message, Throwable cause) { - super(message, - cause); - } -} diff --git a/src/main/java/pro/verron/docxstamper/api/LoadingOpcStamper.java b/src/main/java/pro/verron/docxstamper/api/LoadingOpcStamper.java index 9b165407..6754c7d3 100644 --- a/src/main/java/pro/verron/docxstamper/api/LoadingOpcStamper.java +++ b/src/main/java/pro/verron/docxstamper/api/LoadingOpcStamper.java @@ -34,13 +34,13 @@ public LoadingOpcStamper( * @param inputStream template to stamp * @param context context to use for stamping * @param outputStream output stream to write the result to - * @throws DocxStamperException if the stamping fails + * @throws OpcStamperException if the stamping fails */ public void stamp( InputStream inputStream, Object context, OutputStream outputStream - ) throws DocxStamperException { + ) throws OpcStamperException { T mlPackage = loader.apply(inputStream); stamper.stamp(mlPackage, context, outputStream); } diff --git a/src/main/java/pro/verron/docxstamper/api/OpcStamper.java b/src/main/java/pro/verron/docxstamper/api/OpcStamper.java index 49499578..8083c19f 100644 --- a/src/main/java/pro/verron/docxstamper/api/OpcStamper.java +++ b/src/main/java/pro/verron/docxstamper/api/OpcStamper.java @@ -20,7 +20,11 @@ public interface OpcStamper { * @param template template to stamp * @param context context to use for stamping * @param outputStream output stream to write the result to - * @throws DocxStamperException if the stamping fails + * @throws OpcStamperException if the stamping fails */ - void stamp(T template, Object context, OutputStream outputStream) throws DocxStamperException; + void stamp( + T template, + Object context, + OutputStream outputStream + ) throws OpcStamperException; } diff --git a/src/main/java/pro/verron/docxstamper/api/DocxStamperConfiguration.java b/src/main/java/pro/verron/docxstamper/api/OpcStamperConfiguration.java similarity index 71% rename from src/main/java/pro/verron/docxstamper/api/DocxStamperConfiguration.java rename to src/main/java/pro/verron/docxstamper/api/OpcStamperConfiguration.java index 2a52b1f7..c756eaa2 100644 --- a/src/main/java/pro/verron/docxstamper/api/DocxStamperConfiguration.java +++ b/src/main/java/pro/verron/docxstamper/api/OpcStamperConfiguration.java @@ -9,40 +9,40 @@ import java.util.Optional; import java.util.function.Function; -public interface DocxStamperConfiguration { +public interface OpcStamperConfiguration { @Deprecated(since = "1.6.7") Optional nullReplacementValue(); boolean isFailOnUnresolvedExpression(); - DocxStamperConfiguration setFailOnUnresolvedExpression(boolean failOnUnresolvedExpression); + OpcStamperConfiguration setFailOnUnresolvedExpression(boolean failOnUnresolvedExpression); @Deprecated(since = "1.6.7", forRemoval = true) - DocxStamperConfiguration nullValuesDefault(String nullValuesDefault); + OpcStamperConfiguration nullValuesDefault(String nullValuesDefault); @Deprecated(since = "1.6.7", forRemoval = true) - DocxStamperConfiguration replaceNullValues(boolean replaceNullValues); + OpcStamperConfiguration replaceNullValues(boolean replaceNullValues); - DocxStamperConfiguration unresolvedExpressionsDefaultValue(String unresolvedExpressionsDefaultValue); + OpcStamperConfiguration unresolvedExpressionsDefaultValue(String unresolvedExpressionsDefaultValue); - DocxStamperConfiguration replaceUnresolvedExpressions( + OpcStamperConfiguration replaceUnresolvedExpressions( boolean replaceUnresolvedExpressions ); - DocxStamperConfiguration leaveEmptyOnExpressionError( + OpcStamperConfiguration leaveEmptyOnExpressionError( boolean leaveEmpty ); @Deprecated(since = "1.6.7", forRemoval = true) - DocxStamperConfiguration addTypeResolver( + OpcStamperConfiguration addTypeResolver( Class resolvedType, ITypeResolver resolver ); - DocxStamperConfiguration exposeInterfaceToExpressionLanguage( + OpcStamperConfiguration exposeInterfaceToExpressionLanguage( Class interfaceClass, Object implementation ); - DocxStamperConfiguration addCommentProcessor( + OpcStamperConfiguration addCommentProcessor( Class interfaceClass, Function commentProcessorFactory ); @@ -60,19 +60,19 @@ DocxStamperConfiguration addCommentProcessor( String getLineBreakPlaceholder(); - DocxStamperConfiguration setLineBreakPlaceholder( + OpcStamperConfiguration setLineBreakPlaceholder( @NonNull String lineBreakPlaceholder ); EvaluationContextConfigurer getEvaluationContextConfigurer(); - DocxStamperConfiguration setEvaluationContextConfigurer( + OpcStamperConfiguration setEvaluationContextConfigurer( EvaluationContextConfigurer evaluationContextConfigurer ); SpelParserConfiguration getSpelParserConfiguration(); - DocxStamperConfiguration setSpelParserConfiguration( + OpcStamperConfiguration setSpelParserConfiguration( SpelParserConfiguration spelParserConfiguration ); @@ -85,7 +85,7 @@ DocxStamperConfiguration setSpelParserConfiguration( ITypeResolver getDefaultTypeResolver(); @Deprecated(since = "1.6.7", forRemoval = true) - DocxStamperConfiguration setDefaultTypeResolver( + OpcStamperConfiguration setDefaultTypeResolver( ITypeResolver defaultResolver ); @@ -101,11 +101,11 @@ DocxStamperConfiguration setDefaultTypeResolver( List getResolvers(); - DocxStamperConfiguration setResolvers( + OpcStamperConfiguration setResolvers( List resolvers ); - DocxStamperConfiguration addResolver( + OpcStamperConfiguration addResolver( ObjectResolver resolver ); } diff --git a/src/main/java/pro/verron/docxstamper/api/OpcStamperException.java b/src/main/java/pro/verron/docxstamper/api/OpcStamperException.java new file mode 100644 index 00000000..3b12c0be --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/api/OpcStamperException.java @@ -0,0 +1,13 @@ +package pro.verron.docxstamper.api; + +public class OpcStamperException + extends RuntimeException { + public OpcStamperException(String message) {super(message);} + + public OpcStamperException(Throwable cause) {super(cause);} + + public OpcStamperException(String message, Throwable cause) { + super(message, + cause); + } +} diff --git a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java index 29ff14a9..30257419 100644 --- a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java +++ b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java @@ -7,14 +7,13 @@ import org.docx4j.openpackaging.parts.PartName; import org.docx4j.openpackaging.parts.WordprocessingML.CommentsPart; import org.docx4j.wml.*; -import org.docx4j.wml.Comments.Comment; import org.jvnet.jaxb2_commons.ppp.Child; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.util.walk.BaseDocumentWalker; import org.wickedsource.docxstamper.util.walk.DocumentWalker; -import pro.verron.docxstamper.api.CommentWrapper; +import pro.verron.docxstamper.api.Comment; import pro.verron.docxstamper.api.Placeholder; import java.math.BigInteger; @@ -36,7 +35,8 @@ public class CommentUtil { private static final Logger logger = LoggerFactory.getLogger(CommentUtil.class); private static final String WORD_COMMENTS_PART_NAME = "/word/comments.xml"; - private CommentUtil() { + // TODO: Move to private for next version + protected CommentUtil() { throw new DocxStamperException("Utility class shouldn't be instantiated"); } @@ -47,7 +47,7 @@ private CommentUtil() { * @param document the document that contains the object. * @return Optional of the comment, if found, Optional.empty() otherwise. */ - public static Optional getCommentAround( + public static Optional getCommentAround( R run, WordprocessingMLPackage document ) { if (run == null) return Optional.empty(); @@ -64,7 +64,7 @@ public static Optional getCommentAround( } } - private static Optional getComment( + private static Optional getComment( R run, WordprocessingMLPackage document, ContentAccessor parent ) throws Docx4JException { CommentRangeStart possibleComment = null; @@ -105,7 +105,7 @@ else if (possibleComment != null && foundChild && unwrap( * @return an Optional containing the Comment if found, or an empty Optional if not found * @throws Docx4JException if an error occurs while searching for the comment */ - public static Optional findComment( + public static Optional findComment( WordprocessingMLPackage document, BigInteger id ) throws Docx4JException { var name = new PartName(WORD_COMMENTS_PART_NAME); @@ -133,7 +133,8 @@ public static Optional findComment( public static Placeholder getCommentStringFor( ContentAccessor object, WordprocessingMLPackage document ) { - Comment comment = getCommentFor(object, document).orElseThrow(); + Comments.Comment comment = getCommentFor(object, + document).orElseThrow(); return getCommentString(comment); } @@ -148,7 +149,7 @@ public static Placeholder getCommentStringFor( * @return the concatenated string of all text paragraphs within the * comment or null if the specified object is not commented. */ - public static Optional getCommentFor( + public static Optional getCommentFor( ContentAccessor object, WordprocessingMLPackage document ) { for (Object contentObject : object.getContent()) { @@ -174,7 +175,7 @@ public static Optional getCommentFor( e); } - for (Comment comment : comments.getComment()) { + for (Comments.Comment comment : comments.getComment()) { if (comment.getId() .equals(id)) { return Optional.of(comment); @@ -187,26 +188,26 @@ public static Optional getCommentFor( /** * Returns the string value of the specified comment object. * - * @param comment a {@link Comment} object + * @param comment a {@link Comments.Comment} object * @return a {@link String} object */ - public static Placeholder getCommentString(Comment comment) { + public static Placeholder getCommentString(Comments.Comment comment) { StringBuilder builder = new StringBuilder(); for (Object commentChildObject : comment.getContent()) { if (commentChildObject instanceof P p) { - builder.append(new DefaultParagraph(p).asString()); + builder.append(new StandardParagraph(p).asString()); } } String string = builder.toString(); - return Expressions.raw(string); + return Placeholders.raw(string); } /** * Returns the string value of the specified comment object. * - * @param comment a {@link CommentWrapper} object + * @param comment a {@link Comment} object */ - public static void deleteComment(CommentWrapper comment) { + public static void deleteComment(Comment comment) { CommentRangeEnd end = comment.getCommentRangeEnd(); if (end != null) { ContentAccessor endParent = (ContentAccessor) end.getParent(); @@ -227,6 +228,22 @@ public static void deleteComment(CommentWrapper comment) { } } + /** + * Extracts all comments from the given document. + * + * @param document the document to extract comments from. + * @return a map of all comments, with the key being the comment id. + */ + public static Map getComments( + WordprocessingMLPackage document + ) { + Map rootComments = new HashMap<>(); + Map allComments = new HashMap<>(); + collectCommentRanges(rootComments, allComments, document); + collectComments(allComments, document); + return cleanMalformedComments(rootComments); + } + /** * Returns the string value of the specified comment object. * @@ -261,24 +278,8 @@ public static void deleteCommentFromElement( items.removeAll(elementsToRemove); } - /** - * Extracts all comments from the given document. - * - * @param document the document to extract comments from. - * @return a map of all comments, with the key being the comment id. - */ - public static Map getComments( - WordprocessingMLPackage document - ) { - Map rootComments = new HashMap<>(); - Map allComments = new HashMap<>(); - collectCommentRanges(rootComments, allComments, document); - collectComments(allComments, document); - return cleanMalformedComments(rootComments); - } - - private static Map cleanMalformedComments(Map rootComments) { - Map filteredCommentEntries = new HashMap<>(); + private static Map cleanMalformedComments(Map rootComments) { + Map filteredCommentEntries = new HashMap<>(); rootComments.forEach((key, comment) -> { if (isCommentMalformed(comment)) { @@ -293,7 +294,7 @@ private static Map cleanMalformedComments(Map cleanMalformedComments(Set children) { + private static Set cleanMalformedComments(Set children) { return children.stream() .filter(comment -> { if (isCommentMalformed(comment)) { @@ -308,7 +309,7 @@ private static Set cleanMalformedComments(Set ch .collect(toSet()); } - private static String getCommentContent(CommentWrapper comment) { + private static String getCommentContent(Comment comment) { return comment.getComment() != null ? comment.getComment() .getContent() .stream() @@ -316,66 +317,66 @@ private static String getCommentContent(CommentWrapper comment) { .collect(Collectors.joining("")) : ""; } - private static boolean isCommentMalformed(CommentWrapper comment) { + private static boolean isCommentMalformed(Comment comment) { return comment.getCommentRangeStart() == null || comment.getCommentRangeEnd() == null || comment.getComment() == null; } private static void collectCommentRanges( - Map rootComments, - Map allComments, + Map rootComments, + Map allComments, WordprocessingMLPackage document ) { - Queue stack = Collections.asLifoQueue(new ArrayDeque<>()); + Queue stack = Collections.asLifoQueue(new ArrayDeque<>()); DocumentWalker documentWalker = new BaseDocumentWalker(document.getMainDocumentPart()) { @Override protected void onCommentRangeStart(CommentRangeStart commentRangeStart) { - CommentWrapper commentWrapper = allComments.get( + Comment comment = allComments.get( commentRangeStart.getId()); - if (commentWrapper == null) { - commentWrapper = new DefaultCommentWrapper(); - allComments.put(commentRangeStart.getId(), commentWrapper); + if (comment == null) { + comment = new StandardComment(); + allComments.put(commentRangeStart.getId(), comment); if (stack.isEmpty()) { rootComments.put(commentRangeStart.getId(), - commentWrapper); + comment); } else { stack.peek() .getChildren() - .add(commentWrapper); + .add(comment); } } - commentWrapper.setCommentRangeStart(commentRangeStart); - stack.add(commentWrapper); + comment.setCommentRangeStart(commentRangeStart); + stack.add(comment); } @Override protected void onCommentRangeEnd(CommentRangeEnd commentRangeEnd) { - CommentWrapper commentWrapper = allComments.get(commentRangeEnd.getId()); - if (commentWrapper == null) throw new DocxStamperException( + Comment comment = allComments.get(commentRangeEnd.getId()); + if (comment == null) throw new DocxStamperException( "Found a comment range end before the comment range start !"); - commentWrapper.setCommentRangeEnd(commentRangeEnd); + comment.setCommentRangeEnd(commentRangeEnd); if (stack.isEmpty()) return; if (stack.peek() - .equals(commentWrapper)) stack.remove(); + .equals(comment)) stack.remove(); else throw new DocxStamperException( "Cannot figure which comment contains the other !"); } @Override protected void onCommentReference(R.CommentReference commentReference) { - CommentWrapper commentWrapper = allComments.get(commentReference.getId()); - if (commentWrapper == null) throw new DocxStamperException( + Comment comment = allComments.get(commentReference.getId()); + if (comment == null) throw new DocxStamperException( "Found a comment reference before the comment range start !"); - commentWrapper.setCommentReference(commentReference); + comment.setCommentReference(commentReference); } }; documentWalker.walk(); } private static void collectComments( - Map allComments, + Map allComments, WordprocessingMLPackage document ) { try { @@ -393,5 +394,4 @@ private static void collectComments( throw new IllegalStateException(e); } } - } diff --git a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java index 621d0657..e28e8673 100644 --- a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java +++ b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java @@ -9,10 +9,10 @@ import org.slf4j.LoggerFactory; import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.SpelParseException; -import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.el.ExpressionResolver; import org.wickedsource.docxstamper.util.RunUtil; import org.wickedsource.docxstamper.util.walk.BaseCoordinatesWalker; +import pro.verron.docxstamper.api.OpcStamperException; import pro.verron.docxstamper.api.Paragraph; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.api.Placeholder; @@ -88,7 +88,7 @@ public void resolveExpressions( @Override protected void onParagraph(P paragraph) { resolveExpressionsForParagraph( - new DefaultParagraph(paragraph), + new StandardParagraph(paragraph), expressionContext, document); } @@ -108,7 +108,7 @@ public void resolveExpressionsForParagraph( Object context, WordprocessingMLPackage document ) { - var expressions = Expressions.findVariables(paragraph); + var expressions = Placeholders.findVariables(paragraph); for (var expression : expressions) { try { var resolution = resolver.resolve(expression, context); @@ -119,7 +119,7 @@ public void resolveExpressionsForParagraph( if (failOnUnresolvedExpression) { String message = "Expression %s could not be resolved against context of type %s" .formatted(expression, context.getClass()); - throw new DocxStamperException(message, e); + throw new OpcStamperException(message, e); } else if (leaveEmptyOnExpressionError) { log.warn( "Expression {} could not be resolved against context root of type {}. Reason: {}. Set log level to TRACE to view Stacktrace.", diff --git a/src/main/java/pro/verron/docxstamper/core/Expressions.java b/src/main/java/pro/verron/docxstamper/core/Placeholders.java similarity index 84% rename from src/main/java/pro/verron/docxstamper/core/Expressions.java rename to src/main/java/pro/verron/docxstamper/core/Placeholders.java index 94761e37..be634790 100644 --- a/src/main/java/pro/verron/docxstamper/core/Expressions.java +++ b/src/main/java/pro/verron/docxstamper/core/Placeholders.java @@ -3,8 +3,8 @@ import org.wickedsource.docxstamper.api.DocxStamperException; import pro.verron.docxstamper.api.Paragraph; import pro.verron.docxstamper.api.Placeholder; -import pro.verron.docxstamper.core.expression.ExpressionFinder; import pro.verron.docxstamper.core.expression.Matcher; +import pro.verron.docxstamper.core.expression.PlaceholderFinder; import java.util.List; import java.util.regex.Pattern; @@ -16,7 +16,7 @@ * The findProcessors() method uses PROC_FINDER to find processor expressions in a given text and returns a list of found expressions. * The raw() method creates a new Expression object using the RAW_MATCHER and a specified text. */ -public class Expressions { +public class Placeholders { /** * A regular expression pattern matching processor expressions. * The pattern search for expressions starting with '#{' and ending with @@ -33,8 +33,8 @@ public class Expressions { * An ExpressionFinder to find processor expressions. * It is initialized with a specified pattern and matcher. */ - private static final ExpressionFinder PROC_FINDER = - new ExpressionFinder(PROC_PATTERN, PROC_MATCHER); + private static final PlaceholderFinder PROC_FINDER = + new PlaceholderFinder(PROC_PATTERN, PROC_MATCHER); /** * A regular expression pattern matching processor expressions. @@ -52,8 +52,8 @@ public class Expressions { * An ExpressionFinder to find variable expressions. * It is initialized with a specified pattern and matcher. */ - private static final ExpressionFinder VAR_FINDER = - new ExpressionFinder(VAR_PATTERN, VAR_MATCHER); + private static final PlaceholderFinder VAR_FINDER = + new PlaceholderFinder(VAR_PATTERN, VAR_MATCHER); /** * A Matcher matching raw expressions. * It is typically used to wrap raw expressions that do not have a @@ -61,7 +61,7 @@ public class Expressions { */ private static final Matcher RAW_MATCHER = new Matcher("", ""); - private Expressions() { + private Placeholders() { throw new DocxStamperException( "Utility classes should not be instantiated!"); } @@ -70,7 +70,7 @@ private Expressions() { * Finds variable expressions in a given text. * * @param text the text to search for variable expressions - * @return a list of found variable expressions as {@link DefaultPlaceholder} objects + * @return a list of found variable expressions as {@link StandardPlaceholder} objects */ public static List findVariables(String text) { return VAR_FINDER.find(text); @@ -84,7 +84,7 @@ public static List findVariables(Paragraph paragraph) { * Finds processors expressions in a given text. * * @param text the text to search for processor expressions - * @return a list of found processor expressions as {@link DefaultPlaceholder} + * @return a list of found processor expressions as {@link StandardPlaceholder} * objects */ public static List findProcessors(String text) { @@ -92,6 +92,6 @@ public static List findProcessors(String text) { } public static Placeholder raw(String text) { - return new DefaultPlaceholder(RAW_MATCHER, text); + return new StandardPlaceholder(RAW_MATCHER, text); } } diff --git a/src/main/java/pro/verron/docxstamper/core/DefaultCommentWrapper.java b/src/main/java/pro/verron/docxstamper/core/StandardComment.java similarity index 90% rename from src/main/java/pro/verron/docxstamper/core/DefaultCommentWrapper.java rename to src/main/java/pro/verron/docxstamper/core/StandardComment.java index 8bae0a62..1cffed8c 100644 --- a/src/main/java/pro/verron/docxstamper/core/DefaultCommentWrapper.java +++ b/src/main/java/pro/verron/docxstamper/core/StandardComment.java @@ -6,12 +6,11 @@ import org.docx4j.openpackaging.parts.WordprocessingML.CommentsPart; import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart; import org.docx4j.wml.*; -import org.docx4j.wml.Comments.Comment; import org.docx4j.wml.R.CommentReference; import org.jvnet.jaxb2_commons.ppp.Child; import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.util.DocumentUtil; -import pro.verron.docxstamper.api.CommentWrapper; +import pro.verron.docxstamper.api.Comment; import java.util.*; import java.util.stream.Collectors; @@ -24,17 +23,26 @@ * @version ${version} * @since 1.0.2 */ -public class DefaultCommentWrapper - implements CommentWrapper { +public class StandardComment + implements Comment { - private final Set children = new HashSet<>(); - private Comment comment; + private final Set children = new HashSet<>(); + private Comments.Comment comment; private CommentRangeStart commentRangeStart; private CommentRangeEnd commentRangeEnd; private CommentReference commentReference; - public void setComment(Comment comment) { - this.comment = comment; + private void extractedSubComments( + List commentList, + Set commentChildren + ) { + Queue q = new ArrayDeque<>(commentChildren); + while (!q.isEmpty()) { + Comment element = q.remove(); + commentList.add(element.getComment()); + if (element.getChildren() != null) + q.addAll(element.getChildren()); + } } public void setCommentRangeStart(CommentRangeStart commentRangeStart) { @@ -49,8 +57,14 @@ public void setCommentReference(CommentReference commentReference) { this.commentReference = commentReference; } - public void setChildren(Set children) { - this.children.addAll(children); + /** + *

Getter for the field children.

+ * + * @return a {@link Set} object + */ + @Override + public Set getChildren() { + return children; } /** @@ -125,14 +139,8 @@ private void removeCommentAnchorsFromFinalElements(List finalRepeatEleme CommentUtil.deleteCommentFromElement(fakeBody.getContent(), getComment().getId()); } - private void extractedSubComments(List commentList, Set commentWrapperChildren) { - Queue q = new ArrayDeque<>(commentWrapperChildren); - while (!q.isEmpty()) { - CommentWrapper element = q.remove(); - commentList.add(element.getComment()); - if (element.getChildren() != null) - q.addAll(element.getChildren()); - } + public void setChildren(Set children) { + this.children.addAll(children); } /** @@ -219,23 +227,17 @@ public CommentReference getCommentReference() { return commentReference; } - /** - *

Getter for the field children.

- * - * @return a {@link Set} object - */ - @Override - public Set getChildren() { - return children; - } - /** *

Getter for the field comment.

* - * @return a {@link Comment} object + * @return a {@link Comments.Comment} object */ @Override - public Comment getComment() { + public Comments.Comment getComment() { return comment; } + + public void setComment(Comments.Comment comment) { + this.comment = comment; + } } diff --git a/src/main/java/pro/verron/docxstamper/core/DefaultParagraph.java b/src/main/java/pro/verron/docxstamper/core/StandardParagraph.java similarity index 99% rename from src/main/java/pro/verron/docxstamper/core/DefaultParagraph.java rename to src/main/java/pro/verron/docxstamper/core/StandardParagraph.java index 56558fd1..bc30e81d 100644 --- a/src/main/java/pro/verron/docxstamper/core/DefaultParagraph.java +++ b/src/main/java/pro/verron/docxstamper/core/StandardParagraph.java @@ -26,7 +26,7 @@ * @version ${version} * @since 1.0.8 */ -public class DefaultParagraph +public class StandardParagraph implements Paragraph { private final List runs = new ArrayList<>(); private final P paragraph; @@ -37,7 +37,7 @@ public class DefaultParagraph * * @param paragraph the paragraph to wrap. */ - public DefaultParagraph(P paragraph) { + public StandardParagraph(P paragraph) { this.paragraph = paragraph; recalculateRuns(); } diff --git a/src/main/java/pro/verron/docxstamper/core/DefaultPlaceholder.java b/src/main/java/pro/verron/docxstamper/core/StandardPlaceholder.java similarity index 94% rename from src/main/java/pro/verron/docxstamper/core/DefaultPlaceholder.java rename to src/main/java/pro/verron/docxstamper/core/StandardPlaceholder.java index 5a00fa96..a61684d0 100644 --- a/src/main/java/pro/verron/docxstamper/core/DefaultPlaceholder.java +++ b/src/main/java/pro/verron/docxstamper/core/StandardPlaceholder.java @@ -6,7 +6,7 @@ /** * Represents an expression with a configured Matcher. */ -public record DefaultPlaceholder( +public record StandardPlaceholder( Matcher matcher, String expression ) diff --git a/src/main/java/pro/verron/docxstamper/core/expression/ExpressionFinder.java b/src/main/java/pro/verron/docxstamper/core/expression/PlaceholderFinder.java similarity index 88% rename from src/main/java/pro/verron/docxstamper/core/expression/ExpressionFinder.java rename to src/main/java/pro/verron/docxstamper/core/expression/PlaceholderFinder.java index 7b33ab81..29b3557a 100644 --- a/src/main/java/pro/verron/docxstamper/core/expression/ExpressionFinder.java +++ b/src/main/java/pro/verron/docxstamper/core/expression/PlaceholderFinder.java @@ -1,7 +1,7 @@ package pro.verron.docxstamper.core.expression; import pro.verron.docxstamper.api.Placeholder; -import pro.verron.docxstamper.core.DefaultPlaceholder; +import pro.verron.docxstamper.core.StandardPlaceholder; import java.util.ArrayList; import java.util.List; @@ -16,7 +16,7 @@ * to determine if an expression matches the specified prefix and suffix, * and the Expression class to represent each found expression. */ -public record ExpressionFinder( +public record PlaceholderFinder( Pattern pattern, Matcher matcher ) { @@ -34,7 +34,7 @@ public List find(String text) { List matches = new ArrayList<>(); while (matcher.find(index)) { String match = matcher.group(); - matches.add(new DefaultPlaceholder(this.matcher, match)); + matches.add(new StandardPlaceholder(this.matcher, match)); index = matcher.end(); } return matches; diff --git a/src/main/java/pro/verron/docxstamper/preset/CommentProcessorFactory.java b/src/main/java/pro/verron/docxstamper/preset/CommentProcessorFactory.java index 17caeebb..d9bc98de 100644 --- a/src/main/java/pro/verron/docxstamper/preset/CommentProcessorFactory.java +++ b/src/main/java/pro/verron/docxstamper/preset/CommentProcessorFactory.java @@ -10,8 +10,8 @@ import org.wickedsource.docxstamper.processor.replaceExpression.ReplaceWithProcessor; import org.wickedsource.docxstamper.processor.table.TableResolver; import pro.verron.docxstamper.api.CommentProcessor; -import pro.verron.docxstamper.api.DocxStamperConfiguration; import pro.verron.docxstamper.api.OpcStamper; +import pro.verron.docxstamper.api.OpcStamperConfiguration; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.core.PlaceholderReplacer; @@ -23,14 +23,14 @@ * @since 1.6.4 */ public class CommentProcessorFactory { - private final DocxStamperConfiguration configuration; + private final OpcStamperConfiguration configuration; /** * Creates a new CommentProcessorFactory. * * @param configuration the configuration to use for the created processors. */ - public CommentProcessorFactory(DocxStamperConfiguration configuration) { + public CommentProcessorFactory(OpcStamperConfiguration configuration) { this.configuration = configuration; } diff --git a/src/main/java/pro/verron/docxstamper/preset/Configurations.java b/src/main/java/pro/verron/docxstamper/preset/Configurations.java deleted file mode 100644 index 9f1e8c4f..00000000 --- a/src/main/java/pro/verron/docxstamper/preset/Configurations.java +++ /dev/null @@ -1,9 +0,0 @@ -package pro.verron.docxstamper.preset; - -import pro.verron.docxstamper.api.DocxStamperConfiguration; - -public class Configurations { - public static DocxStamperConfiguration standard() { - return new org.wickedsource.docxstamper.DocxStamperConfiguration(); - } -} diff --git a/src/main/java/pro/verron/docxstamper/preset/OpcStamperConfigurations.java b/src/main/java/pro/verron/docxstamper/preset/OpcStamperConfigurations.java new file mode 100644 index 00000000..d7229969 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/preset/OpcStamperConfigurations.java @@ -0,0 +1,18 @@ +package pro.verron.docxstamper.preset; + +import org.wickedsource.docxstamper.preprocessor.MergeSameStyleRuns; +import org.wickedsource.docxstamper.preprocessor.RemoveProofErrors; +import pro.verron.docxstamper.api.OpcStamperConfiguration; + +public class OpcStamperConfigurations { + public static OpcStamperConfiguration standard() { + return new org.wickedsource.docxstamper.DocxStamperConfiguration(); + } + + static OpcStamperConfiguration standardWithPreprocessing() { + var configuration = standard(); + configuration.addPreprocessor(new RemoveProofErrors()); + configuration.addPreprocessor(new MergeSameStyleRuns()); + return configuration; + } +} diff --git a/src/main/java/pro/verron/docxstamper/preset/Stampers.java b/src/main/java/pro/verron/docxstamper/preset/OpcStampers.java similarity index 50% rename from src/main/java/pro/verron/docxstamper/preset/Stampers.java rename to src/main/java/pro/verron/docxstamper/preset/OpcStampers.java index b9bf890f..7b264efd 100644 --- a/src/main/java/pro/verron/docxstamper/preset/Stampers.java +++ b/src/main/java/pro/verron/docxstamper/preset/OpcStampers.java @@ -2,10 +2,10 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.wickedsource.docxstamper.DocxStamper; -import org.wickedsource.docxstamper.DocxStamperConfiguration; import org.wickedsource.docxstamper.preprocessor.MergeSameStyleRuns; import org.wickedsource.docxstamper.preprocessor.RemoveProofErrors; import pro.verron.docxstamper.api.OpcStamper; +import pro.verron.docxstamper.api.OpcStamperConfiguration; /** * Main class of the docx-stamper library. @@ -17,9 +17,11 @@ * @version ${version} * @since 1.6.4 */ -public class Stampers { +public class OpcStampers { - public static OpcStamper from(pro.verron.docxstamper.api.DocxStamperConfiguration config) { + public static OpcStamper docxStamper( + OpcStamperConfiguration config + ) { return new DocxStamper<>(config); } @@ -29,21 +31,8 @@ public static OpcStamper from(pro.verron.docxstamper.ap * * @return a new DocxStamper */ - public OpcStamper newDocxStamper() { - DocxStamperConfiguration configuration = new DocxStamperConfiguration(); - configuration.addPreprocessor(new RemoveProofErrors()); - configuration.addPreprocessor(new MergeSameStyleRuns()); - return new DocxStamper<>(configuration); + public OpcStamper docxStamper() { + return new DocxStamper<>(OpcStamperConfigurations.standardWithPreprocessing()); } - /** - * Creates a new DocxStamper with the default configuration. - * Does not add any preprocessors. - * - * @return a new DocxStamper - */ - public OpcStamper nopreprocessingDocxStamper() { - DocxStamperConfiguration configuration = new DocxStamperConfiguration(); - return new DocxStamper<>(configuration); - } } diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/Resolvers.java b/src/main/java/pro/verron/docxstamper/preset/resolver/Resolvers.java new file mode 100644 index 00000000..7172e30e --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/Resolvers.java @@ -0,0 +1,13 @@ +package pro.verron.docxstamper.preset.resolver; + +/** + * @deprecated since 1.6.8, This class has been deprecated in the effort + * of the library modularization. + * It is recommended to use the + * {@link pro.verron.docxstamper.preset.Resolvers} class instead. + * This class will be removed in the future releases of the module. + */ +@Deprecated(since = "1.6.8", forRemoval = true) +public class Resolvers + extends pro.verron.docxstamper.preset.Resolvers { +} diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/StringResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/StringResolver.java new file mode 100644 index 00000000..a1631c99 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/StringResolver.java @@ -0,0 +1,21 @@ +package pro.verron.docxstamper.preset.resolver; + +/** + * @deprecated since 1.6.8, This class has been deprecated in the effort + * of the library modularization. + * It is recommended to use the + * {@link pro.verron.docxstamper.api.StringResolver} class instead. + * This class will be removed in the future releases of the module. + */ +@Deprecated(since = "1.6.8", forRemoval = true) +public abstract class StringResolver + extends pro.verron.docxstamper.api.StringResolver { + /** + * Creates a new StringResolver with the given type. + * + * @param type the type of object to be resolved + */ + protected StringResolver(Class type) { + super(type); + } +} diff --git a/src/test/java/module-info.java b/src/test/java/module-info.java index 78ac15f7..5cdc084e 100644 --- a/src/test/java/module-info.java +++ b/src/test/java/module-info.java @@ -1,5 +1,5 @@ -module pro.verron.docxstamper.test { - requires transitive pro.verron.docxstamper; +module pro.verron.opcstamper.test { + requires transitive pro.verron.opcstamper; requires org.junit.jupiter.api; requires org.junit.jupiter.params; @@ -15,9 +15,4 @@ requires jakarta.xml.bind; opens pro.verron.docxstamper.test; - - // exports pro.verron.docxstamper.test; - // exports pro.verron.docxstamper.test.commentProcessors; - // exports pro.verron.docxstamper.test.utils.context; - // exports org.wickedsource.docxstamper.test; } diff --git a/src/test/java/pro/verron/docxstamper/test/CustomCommentProcessor.java b/src/test/java/pro/verron/docxstamper/test/CustomCommentProcessor.java index 7e775789..83e1ea80 100644 --- a/src/test/java/pro/verron/docxstamper/test/CustomCommentProcessor.java +++ b/src/test/java/pro/verron/docxstamper/test/CustomCommentProcessor.java @@ -5,8 +5,8 @@ import org.docx4j.wml.P; import org.docx4j.wml.R; import pro.verron.docxstamper.api.AbstractCommentProcessor; +import pro.verron.docxstamper.api.Comment; import pro.verron.docxstamper.api.CommentProcessor; -import pro.verron.docxstamper.api.CommentWrapper; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import java.util.ArrayList; @@ -96,7 +96,7 @@ public void setParagraph(P paragraph) { * {@inheritDoc} */ @Override - public void setCurrentCommentWrapper(CommentWrapper commentWrapper) { + public void setCurrentCommentWrapper(Comment comment) { } /** diff --git a/src/test/java/pro/verron/docxstamper/test/DefaultTests.java b/src/test/java/pro/verron/docxstamper/test/DefaultTests.java index f144af3b..a6c8745d 100644 --- a/src/test/java/pro/verron/docxstamper/test/DefaultTests.java +++ b/src/test/java/pro/verron/docxstamper/test/DefaultTests.java @@ -6,10 +6,10 @@ import org.junit.jupiter.params.provider.MethodSource; import org.springframework.context.expression.MapAccessor; import org.springframework.expression.spel.SpelParserConfiguration; -import pro.verron.docxstamper.api.DocxStamperConfiguration; import pro.verron.docxstamper.api.Image; -import pro.verron.docxstamper.preset.Configurations; +import pro.verron.docxstamper.api.OpcStamperConfiguration; import pro.verron.docxstamper.preset.EvaluationContextConfigurers; +import pro.verron.docxstamper.preset.OpcStamperConfigurations; import pro.verron.docxstamper.preset.Resolvers; import java.io.IOException; @@ -54,7 +54,7 @@ public static InputStream getResource(Path path) { private static Arguments replaceWordWithIntegrationTest() { return of("replaceWordWithIntegrationTest", - Configurations.standard(), + OpcStamperConfigurations.standard(), name("Simpsons"), getResource(Path.of("integration", "ReplaceWordWithIntegrationTest.docx")), @@ -67,7 +67,7 @@ private static Arguments replaceWordWithIntegrationTest() { private static Arguments repeatingRows() { return of("Repeating table rows should be possible", - Configurations.standard(), + OpcStamperConfigurations.standard(), roles(role("Homer Simpson", "Dan Castellaneta"), role("Marge Simpson", "Julie Kavner"), role("Bart Simpson", "Nancy Cartwright"), @@ -98,7 +98,7 @@ private static Arguments repeatingRows() { private static Arguments ternary() { return of("Ternary operators should function", - Configurations.standard(), + OpcStamperConfigurations.standard(), name("Homer"), getResource(Path.of("TernaryOperatorTest.docx")), """ @@ -111,7 +111,7 @@ private static Arguments ternary() { private static Arguments whitespaces() { return of("White spaces should be preserved", - Configurations.standard(), + OpcStamperConfigurations.standard(), name("Homer Simpson"), getResource(Path.of("TabsIndentationTest.docx")), """ @@ -121,7 +121,7 @@ private static Arguments whitespaces() { private static Arguments tabulations() { return of("Tabulation should be preserved", - Configurations.standard(), + OpcStamperConfigurations.standard(), name("Homer Simpson"), getResource(Path.of("TabsIndentationTest.docx")), """ @@ -131,7 +131,7 @@ private static Arguments tabulations() { private static Arguments replaceNullExpressionTest() { return of("Do not replace 'null' values", - Configurations.standard() + OpcStamperConfigurations.standard() .addResolver(Resolvers.nullToPlaceholder()), name(null), getResource(Path.of("ReplaceNullExpressionTest.docx")), @@ -140,7 +140,7 @@ private static Arguments replaceNullExpressionTest() { private static Arguments replaceNullExpressionTest2() { return of("Do replace 'null' values", - Configurations.standard() + OpcStamperConfigurations.standard() .addResolver(Resolvers.nullToEmpty()), name(null), getResource(Path.of("ReplaceNullExpressionTest.docx")), @@ -149,7 +149,7 @@ private static Arguments replaceNullExpressionTest2() { private static Arguments repeatTableRowKeepsFormatTest() { return of("repeatTableRowKeepsFormatTest", - Configurations.standard(), + OpcStamperConfigurations.standard(), new Show(List.of(new CharacterRecord(1, "st", "Homer Simpson", @@ -209,7 +209,7 @@ private static Arguments repeatParagraphTest() { ❬There are ❬6❘lang=de-DE❭ characters.❘spacing={after=140,before=0}❭"""; return arguments("repeatParagraphTest", - Configurations.standard(), + OpcStamperConfigurations.standard(), context, template, expected); @@ -234,7 +234,7 @@ private static Arguments repeatDocPartWithImageTestShouldImportImageDataInTheMai rId13:image/png:193.6kB:sha1=t8UNAmo7yJgZJk9g7pLLIb3AvCA=:cy=$d:6120130 """; - var config = Configurations.standard() + var config = OpcStamperConfigurations.standard() .setEvaluationContextConfigurer( ctx -> ctx.addPropertyAccessor(new MapAccessor())); return of( @@ -256,7 +256,7 @@ private static Image getImage(Path path) { private static Arguments repeatDocPartWithImagesInSourceTestshouldReplicateImageFromTheMainDocumentInTheSubTemplate() { return of( "repeatDocPartWithImagesInSourceTestshouldReplicateImageFromTheMainDocumentInTheSubTemplate", - Configurations.standard() + OpcStamperConfigurations.standard() .setEvaluationContextConfigurer( (ctx) -> ctx.addPropertyAccessor(new MapAccessor())), Contexts.subDocPartContext(), @@ -275,7 +275,7 @@ private static Arguments repeatDocPartWithImagesInSourceTestshouldReplicateImage private static Arguments repeatDocPartTest() { return of("repeatDocPartTest", - Configurations.standard(), + OpcStamperConfigurations.standard(), new Characters(List.of( new Role("Homer Simpson", "Dan Castellaneta"), new Role("Marge Simpson", "Julie Kavner"), @@ -316,7 +316,7 @@ private static Arguments repeatDocPartTest() { private static Arguments repeatDocPartNestingTest() { return of("repeatDocPartNestingTest", - Configurations.standard(), + OpcStamperConfigurations.standard(), Contexts.schoolContext(), getResource(Path.of("RepeatDocPartNestingTest.docx")), """ @@ -503,7 +503,7 @@ private static Arguments repeatDocPartAndCommentProcessorsIsolationTest_repeatDo This will stay untouched too."""; - var config = Configurations.standard() + var config = OpcStamperConfigurations.standard() .setEvaluationContextConfigurer( (ctx) -> ctx.addPropertyAccessor(new MapAccessor())); @@ -518,7 +518,7 @@ private static Arguments repeatDocPartAndCommentProcessorsIsolationTest_repeatDo private static Arguments changingPageLayoutTest_shouldKeepSectionBreakOrientationInRepeatParagraphWithoutSectionBreakInsideComment() { return arguments( "changingPageLayoutTest_shouldKeepSectionBreakOrientationInRepeatParagraphWithoutSectionBreakInsideComment", - Configurations.standard() + OpcStamperConfigurations.standard() .setEvaluationContextConfigurer( ctx -> ctx.addPropertyAccessor(new MapAccessor())), Map.of("repeatValues", @@ -558,7 +558,7 @@ Second page is portrait, layout change should survive to repeatParagraph process ❬❘docGrid=xxx,eGHdrFtrReferences=xxx,pgMar=xxx,pgSz={h=11906,orient=landscape,w=16838}❭ Fourth page is set to portrait again."""; - var config = Configurations.standard() + var config = OpcStamperConfigurations.standard() .setEvaluationContextConfigurer( ctx -> ctx.addPropertyAccessor(new MapAccessor())); return arguments( @@ -573,7 +573,7 @@ Second page is portrait, layout change should survive to repeatParagraph process private static Arguments changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithSectionBreaksInsideComment() { return arguments( "changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithSectionBreaksInsideComment", - Configurations.standard() + OpcStamperConfigurations.standard() .setEvaluationContextConfigurer( ctx -> ctx.addPropertyAccessor(new MapAccessor())), Map.of("repeatValues", @@ -599,7 +599,7 @@ Second page is landscape, layout change should survive to repeatDocPart (Marge). private static Arguments changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithSectionBreaksInsideCommentAndTableAsLastElement() { return arguments( "changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithSectionBreaksInsideCommentAndTableAsLastElement", - Configurations.standard() + OpcStamperConfigurations.standard() .setEvaluationContextConfigurer( ctx -> ctx.addPropertyAccessor(new MapAccessor())), Map.of("repeatValues", @@ -630,7 +630,7 @@ Second page is landscape, layout change should survive to repeatDocPart (Marge). private static Arguments changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithoutSectionBreaksInsideComment() { return arguments( "changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithoutSectionBreaksInsideComment", - Configurations.standard() + OpcStamperConfigurations.standard() .setEvaluationContextConfigurer( ctx -> ctx.addPropertyAccessor(new MapAccessor())), Map.of("repeatValues", @@ -669,7 +669,7 @@ private static Arguments conditionalDisplayOfParagraphsTest_processorExpressions return arguments( "conditionalDisplayOfParagraphsTest_processorExpressionsInCommentsAreResolved", - Configurations.standard(), + OpcStamperConfigurations.standard(), context, template, expected); @@ -693,7 +693,7 @@ private static Arguments conditionalDisplayOfParagraphsTest_inlineProcessorExpre """; return arguments( "conditionalDisplayOfParagraphsTest_inlineProcessorExpressionsAreResolved", - Configurations.standard(), + OpcStamperConfigurations.standard(), context, template, expected); @@ -719,7 +719,7 @@ private static Arguments conditionalDisplayOfParagraphsTest_unresolvedInlineProc """; return arguments( "conditionalDisplayOfParagraphsTest_unresolvedInlineProcessorExpressionsAreRemoved", - Configurations.standard(), + OpcStamperConfigurations.standard(), context, template, expected); @@ -739,7 +739,7 @@ private static Arguments conditionalDisplayOfTableRowsTest() { ❬❘spacing={after=140,before=0}❭"""; return arguments("conditionalDisplayOfTableRowsTest", - Configurations.standard(), + OpcStamperConfigurations.standard(), context, template, expected); @@ -763,7 +763,7 @@ private static Arguments conditionalDisplayOfTableBug32Test() { ❬❬This paragraph stays untouched.❘lang=de-DE❭❘spacing={after=140,before=0}❭"""; return arguments("conditionalDisplayOfTableBug32Test", - Configurations.standard(), + OpcStamperConfigurations.standard(), context, template, expected); @@ -787,7 +787,7 @@ private static Arguments conditionalDisplayOfTableTest() { ❬❬This paragraph stays untouched.❘lang=de-DE❭❘lang=de-DE,spacing={after=140,before=0}❭"""; return arguments("conditionalDisplayOfTablesTest", - Configurations.standard(), + OpcStamperConfigurations.standard(), context, template, expected); @@ -801,7 +801,7 @@ private static Arguments customEvaluationContextConfigurerTest_customEvaluationC ❬Custom EvaluationContextConfigurer Test❘spacing={after=120,before=240}❭ ❬❬This paragraph stays untouched.❘lang=de-DE❭❘lang=de-DE❭ ❬The variable foo has the value ❬bar❘lang=de-DE❭.❘spacing={after=140,before=0}❭"""; - var config = Configurations.standard() + var config = OpcStamperConfigurations.standard() .setEvaluationContextConfigurer( evalContext -> evalContext.addPropertyAccessor(new SimpleGetter( "foo", @@ -823,7 +823,7 @@ private static Arguments customExpressionFunctionTest() { ❬❬This paragraph is untouched.❘lang=de-DE❭❘lang=de-DE❭ ❬❬In this paragraph, a custom expression function is used to uppercase a String: |BR|❘lang=de-DE❭❬HOMER SIMPSON❘b=true,lang=de-DE❭❬.❘lang=de-DE❭❘spacing={after=140,before=0}❭ ❬❬To test that custom functions work together with comment expressions, we toggle visibility of this paragraph with a comment expression.❘lang=de-DE❭❘spacing={after=140,before=0}❭"""; - var config = Configurations.standard() + var config = OpcStamperConfigurations.standard() .exposeInterfaceToExpressionLanguage( Functions.UppercaseFunction.class, Functions.upperCase()); @@ -836,7 +836,7 @@ private static Arguments customExpressionFunctionTest() { private static Arguments customTypeResolverTest() { return arguments("customTypeResolverTest", - Configurations.standard() + OpcStamperConfigurations.standard() .addResolver(new CustomTypeResolver()), new Context(new CustomType()), getResource(Path.of("CustomTypeResolverTest.docx")), @@ -857,7 +857,7 @@ private static Arguments dateReplacementTest() { Today is: %s""".formatted(formattedDate); return arguments("dateReplacementTest", - Configurations.standard(), + OpcStamperConfigurations.standard(), context, template, expected); @@ -872,7 +872,7 @@ private static Arguments expressionReplacementInGlobalParagraphsTest() { ❬❬This paragraph is untouched.❘lang=de-DE❭❘lang=de-DE❭ ❬In this paragraph, the variable ❘lang=de-DE❭❬name❘b=true,lang=de-DE❭ should be resolved to the value ❬Homer Simpson❘lang=de-DE❭. ❬❬In this paragraph, the variable ❘lang=de-DE❭❬foo❘b=true,lang=de-DE❭❬ should not be resolved: ${foo}.❘lang=de-DE❭❘spacing={after=140,before=0}❭"""; - DocxStamperConfiguration config = Configurations.standard() + OpcStamperConfiguration config = OpcStamperConfigurations.standard() .setFailOnUnresolvedExpression(false); return arguments("expressionReplacementInGlobalParagraphsTest", config, @@ -899,7 +899,7 @@ private static Arguments expressionReplacementInTablesTest() { ${foo} ❬❘spacing={after=140,before=0}❭"""; - DocxStamperConfiguration config = Configurations.standard() + OpcStamperConfiguration config = OpcStamperConfigurations.standard() .setFailOnUnresolvedExpression(false); return arguments("expressionReplacementInTablesTest", config, @@ -931,7 +931,7 @@ private static Arguments expressionReplacementWithFormattingTest() { ❬It should be white over darkblue: ❬Homer Simpson❘color=FFFFFF,highlight=darkBlue❭❘b=true❭ ❬It should be with header formatting: ❬Homer Simpson❘rStyle=TitreCar❭❘b=true❭"""; return arguments("expressionReplacementWithFormattingTest", - Configurations.standard(), + OpcStamperConfigurations.standard(), context, template, expected); @@ -952,7 +952,7 @@ private static Arguments expressionWithSurroundingSpacesTest() { Before Expression After. ❬Before Expression After.❘spacing={after=140,before=0}❭"""; return arguments("expressionWithSurroundingSpacesTest", - Configurations.standard(), + OpcStamperConfigurations.standard(), spacyContext, template, expected); @@ -967,7 +967,7 @@ private static Arguments expressionReplacementWithCommentTest() { This paragraph is untouched. In this paragraph, the variable ❬name❘b=true❭ should be resolved to the value Homer Simpson. ❬In this paragraph, the variable ❬foo❘b=true❭ should not be resolved: unresolvedValueWithCommentreplaceWordWith(foo).❘spacing={after=140,before=0,line=288,lineRule=AUTO}❭"""; - var config = Configurations.standard() + var config = OpcStamperConfigurations.standard() .setFailOnUnresolvedExpression(false); return arguments("expressionReplacementWithCommentsTest", config, @@ -990,7 +990,7 @@ private static Arguments imageReplacementInGlobalParagraphsTest() { ❬In this paragraph, an image of Mona Lisa is inserted: ❬rId4:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:1276350❘lang=de-DE❭.❘lang=de-DE❭ ❬This paragraph has the image ❬rId5:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:1276350❘lang=de-DE❭ in the middle.❘lang=de-DE,spacing={after=140,before=0}❭"""; return arguments("imageReplacementInGlobalParagraphsTest", - Configurations.standard(), + OpcStamperConfigurations.standard(), context, template, expected); @@ -1008,7 +1008,7 @@ private static Arguments imageReplacementInGlobalParagraphsTestWithMaxWidth() { ❬In this paragraph, an image of Mona Lisa is inserted: ❬rId4:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:635000❘lang=de-DE❭.❘lang=de-DE❭ ❬This paragraph has the image ❬rId5:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:635000❘lang=de-DE❭ in the middle.❘lang=de-DE,spacing={after=140,before=0}❭"""; return arguments("imageReplacementInGlobalParagraphsTestWithMaxWidth", - Configurations.standard(), + OpcStamperConfigurations.standard(), context, template, expected); @@ -1029,7 +1029,7 @@ private static Arguments leaveEmptyOnExpressionErrorTest() { var expected = """ Leave me empty . ❬❘u=single❭"""; - var config = Configurations.standard() + var config = OpcStamperConfigurations.standard() .setFailOnUnresolvedExpression(false) .leaveEmptyOnExpressionError(true); return arguments("leaveEmptyOnExpressionErrorTest", @@ -1040,7 +1040,7 @@ private static Arguments leaveEmptyOnExpressionErrorTest() { } private static Arguments lineBreakReplacementTest() { - var config = Configurations.standard() + var config = OpcStamperConfigurations.standard() .setLineBreakPlaceholder("#"); var context = new Contexts.Name(null); var template = getResource(Path.of("LineBreakReplacementTest.docx")); @@ -1080,7 +1080,7 @@ private static Arguments mapAccessorAndReflectivePropertyAccessorTest_shouldReso Paragraph end """; - var config = Configurations.standard() + var config = OpcStamperConfigurations.standard() .setFailOnUnresolvedExpression(false) .setLineBreakPlaceholder("\n") .addResolver(Resolvers.nullToDefault("N/C")) @@ -1114,7 +1114,7 @@ private static Arguments nullPointerResolutionTest_testWithDefaultSpel() { Deal with: ${nullish.li[2] ?: "Nullish value!!"} """; - var config = Configurations.standard() + var config = OpcStamperConfigurations.standard() .setFailOnUnresolvedExpression(false); return arguments("nullPointerResolutionTest_testWithDefaultSpel", @@ -1127,7 +1127,7 @@ private static Arguments nullPointerResolutionTest_testWithDefaultSpel() { private static Arguments customCommentProcessor() { return arguments( "customCommentProcessor", - Configurations.standard() + OpcStamperConfigurations.standard() .addCommentProcessor( ICustomCommentProcessor.class, CustomCommentProcessor::new), @@ -1160,7 +1160,7 @@ private static Arguments nullPointerResolutionTest_testWithCustomSpel() { // Beware, this configuration only autogrows pojos and java beans, // so it will not work if your type has no default constructor and no setters. - var config = Configurations.standard() + var config = OpcStamperConfigurations.standard() .setSpelParserConfiguration(new SpelParserConfiguration(true, true)) .setEvaluationContextConfigurer(EvaluationContextConfigurers.noopConfigurer()) @@ -1227,7 +1227,7 @@ public static Stream tests() { @ParameterizedTest(name = "{0}") void features( String ignoredName, - DocxStamperConfiguration config, + OpcStamperConfiguration config, Object context, InputStream template, String expected diff --git a/src/test/java/pro/verron/docxstamper/test/FailOnUnresolvedPlaceholderTest.java b/src/test/java/pro/verron/docxstamper/test/FailOnUnresolvedPlaceholderTest.java index 580272c6..cb28c148 100644 --- a/src/test/java/pro/verron/docxstamper/test/FailOnUnresolvedPlaceholderTest.java +++ b/src/test/java/pro/verron/docxstamper/test/FailOnUnresolvedPlaceholderTest.java @@ -1,8 +1,8 @@ package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.api.DocxStamperException; -import pro.verron.docxstamper.preset.Configurations; +import pro.verron.docxstamper.api.OpcStamperException; +import pro.verron.docxstamper.preset.OpcStamperConfigurations; import java.io.IOException; import java.io.InputStream; @@ -22,10 +22,10 @@ void fails() throws IOException { var context = new Name("Homer"); try (var template = getResource(Path.of("FailOnUnresolvedExpressionTest" + ".docx"))) { - var config = Configurations.standard() + var config = OpcStamperConfigurations.standard() .setFailOnUnresolvedExpression(true); var stamper = new TestDocxStamper<>(config); - assertThrows(DocxStamperException.class, + assertThrows(OpcStamperException.class, () -> stamper.stampAndLoad(template, context)); } } @@ -35,7 +35,7 @@ void doesNotFail() throws IOException { Name context = new Name("Homer"); try (InputStream template = getResource(Path.of( "FailOnUnresolvedExpressionTest.docx"))) { - var config = Configurations.standard() + var config = OpcStamperConfigurations.standard() .setFailOnUnresolvedExpression(false); var stamper = new TestDocxStamper<>(config); assertDoesNotThrow(() -> stamper.stampAndLoad(template, context)); diff --git a/src/test/java/pro/verron/docxstamper/test/MultiSectionTest.java b/src/test/java/pro/verron/docxstamper/test/MultiSectionTest.java index f68bf39b..0a3c8e0f 100644 --- a/src/test/java/pro/verron/docxstamper/test/MultiSectionTest.java +++ b/src/test/java/pro/verron/docxstamper/test/MultiSectionTest.java @@ -1,7 +1,7 @@ package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.preset.Configurations; +import pro.verron.docxstamper.preset.OpcStamperConfigurations; import java.nio.file.Path; @@ -17,7 +17,7 @@ class MultiSectionTest { void expressionsInMultipleSections() { var context = new NamesContext("Homer", "Marge"); var template = getResource(Path.of("MultiSectionTest.docx")); - var configuration = Configurations.standard(); + var configuration = OpcStamperConfigurations.standard(); var stamper = new TestDocxStamper(configuration); var actual = stamper.stampAndLoadAndExtract(template, context); String expected = """ diff --git a/src/test/java/pro/verron/docxstamper/test/MultiStampTest.java b/src/test/java/pro/verron/docxstamper/test/MultiStampTest.java index f64288a5..5e5d0df5 100644 --- a/src/test/java/pro/verron/docxstamper/test/MultiStampTest.java +++ b/src/test/java/pro/verron/docxstamper/test/MultiStampTest.java @@ -1,7 +1,7 @@ package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.preset.Configurations; +import pro.verron.docxstamper.preset.OpcStamperConfigurations; import java.nio.file.Path; @@ -16,7 +16,7 @@ class MultiStampTest { @Test void expressionsAreResolvedOnMultiStamp() { - var config = Configurations.standard(); + var config = OpcStamperConfigurations.standard(); var context = names("Homer","Marge","Bart","Lisa","Maggie"); var stamper = new TestDocxStamper<>(config); diff --git a/src/test/java/pro/verron/docxstamper/test/NullPointerResolutionTest.java b/src/test/java/pro/verron/docxstamper/test/NullPointerResolutionTest.java index 33170d4c..5db63139 100644 --- a/src/test/java/pro/verron/docxstamper/test/NullPointerResolutionTest.java +++ b/src/test/java/pro/verron/docxstamper/test/NullPointerResolutionTest.java @@ -1,8 +1,8 @@ package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.api.DocxStamperException; -import pro.verron.docxstamper.preset.Configurations; +import pro.verron.docxstamper.api.OpcStamperException; +import pro.verron.docxstamper.preset.OpcStamperConfigurations; import java.io.IOException; import java.nio.file.Path; @@ -23,10 +23,10 @@ void nullPointerResolutionTest_testThrowingCase() throws IOException { var context = new NullishContext("Fullish1", subContext, null, null); try (var template = getResource(Path.of("NullPointerResolution.docx"))) { - var configuration = Configurations.standard(); + var configuration = OpcStamperConfigurations.standard(); var stamper = new TestDocxStamper(configuration); assertThrows( - DocxStamperException.class, + OpcStamperException.class, () -> stamper.stampAndLoadAndExtract(template, context) ); } diff --git a/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java b/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java index f49b3a13..b271fb9d 100644 --- a/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java +++ b/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java @@ -1,7 +1,7 @@ package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.preset.Configurations; +import pro.verron.docxstamper.preset.OpcStamperConfigurations; import java.nio.file.Path; @@ -18,7 +18,7 @@ void expressionReplacementInHeaderAndFooterTest() { var context = new Name("Homer Simpson"); var template = getResource( Path.of("ExpressionReplacementInHeaderAndFooterTest.docx")); - var configuration = Configurations.standard() + var configuration = OpcStamperConfigurations.standard() .setFailOnUnresolvedExpression(false); var stamper = new TestDocxStamper(configuration); var actual = stamper.stampAndLoadAndExtract(template, context); diff --git a/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java b/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java index dbde8344..13f5a476 100644 --- a/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java +++ b/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java @@ -2,7 +2,7 @@ import org.docx4j.dml.wordprocessingDrawing.Anchor; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.preset.Configurations; +import pro.verron.docxstamper.preset.OpcStamperConfigurations; import java.nio.file.Path; import java.util.List; @@ -20,7 +20,7 @@ void expressionReplacementInTextBoxesTest() { var context = new Name("Bart Simpson"); var template = getResource(Path.of("ExpressionReplacementInTextBoxesTest" + ".docx")); - var configuration = Configurations.standard() + var configuration = OpcStamperConfigurations.standard() .setFailOnUnresolvedExpression(false); var stamper = new TestDocxStamper(configuration); var actual = stamper.stampAndLoadAndExtract(template, context, Anchor.class); diff --git a/src/test/java/pro/verron/docxstamper/test/RepeatDocPartBadPlaceholderTest.java b/src/test/java/pro/verron/docxstamper/test/RepeatDocPartBadPlaceholderTest.java index 9e554d0c..a0474d2d 100644 --- a/src/test/java/pro/verron/docxstamper/test/RepeatDocPartBadPlaceholderTest.java +++ b/src/test/java/pro/verron/docxstamper/test/RepeatDocPartBadPlaceholderTest.java @@ -4,8 +4,8 @@ import org.junit.jupiter.api.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import pro.verron.docxstamper.api.DocxStamperException; -import pro.verron.docxstamper.preset.Configurations; +import pro.verron.docxstamper.api.OpcStamperException; +import pro.verron.docxstamper.preset.OpcStamperConfigurations; import pro.verron.docxstamper.test.Contexts.Characters; import pro.verron.docxstamper.test.Contexts.Role; @@ -35,11 +35,11 @@ public void testBadExpressionShouldNotBlockCallerThread() { List.of(new Role("Homer Simpson", "Dan Castellaneta"), new Role("Marge Simpson", "Julie Kavner"), new Role("Bart Simpson", "Nancy Cartwright"))); - var configuration = Configurations.standard(); + var configuration = OpcStamperConfigurations.standard(); var stamper = new TestDocxStamper<>(configuration); var exception = assertThrows( - DocxStamperException.class, + OpcStamperException.class, () -> stamper.stampAndLoadAndExtract(template, context)); String expectedErrorInfo = "someUnknownField"; diff --git a/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java b/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java index cb66d5cb..888b1387 100644 --- a/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java +++ b/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java @@ -1,8 +1,8 @@ package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.api.DocxStamperException; -import pro.verron.docxstamper.preset.Configurations; +import pro.verron.docxstamper.api.OpcStamperException; +import pro.verron.docxstamper.preset.OpcStamperConfigurations; import java.io.IOException; import java.nio.file.Path; @@ -20,9 +20,11 @@ class SpelInjectionTest { void spelInjectionTest() throws IOException { var context = Contexts.empty(); try (var template = getResource(Path.of("SpelInjectionTest.docx"))) { - var configuration = Configurations.standard(); + var configuration = OpcStamperConfigurations.standard(); var stamper = new TestDocxStamper<>(configuration); - assertThrows(DocxStamperException.class, () -> stamper.stampAndLoadAndExtract(template, context)); + assertThrows(OpcStamperException.class, + () -> stamper.stampAndLoadAndExtract(template, + context)); } assertDoesNotThrow(() -> "Does not throw", "Since VM is still up."); } diff --git a/src/test/java/pro/verron/docxstamper/test/StampTableTest.java b/src/test/java/pro/verron/docxstamper/test/StampTableTest.java index 17513779..38cbec10 100644 --- a/src/test/java/pro/verron/docxstamper/test/StampTableTest.java +++ b/src/test/java/pro/verron/docxstamper/test/StampTableTest.java @@ -1,7 +1,7 @@ package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.preset.Configurations; +import pro.verron.docxstamper.preset.OpcStamperConfigurations; import java.nio.file.Path; import java.util.List; @@ -19,7 +19,7 @@ void stampTableTest() { var testDocx = getResource(Path.of("StampTableTest.docx")); - var configuration = Configurations.standard(); + var configuration = OpcStamperConfigurations.standard(); var stamper = new TestDocxStamper<>(configuration); String string = stamper.stampAndLoadAndExtract( diff --git a/src/test/java/pro/verron/docxstamper/test/Stringifier.java b/src/test/java/pro/verron/docxstamper/test/Stringifier.java index ad4303df..b57d2e4b 100644 --- a/src/test/java/pro/verron/docxstamper/test/Stringifier.java +++ b/src/test/java/pro/verron/docxstamper/test/Stringifier.java @@ -16,7 +16,7 @@ import org.docx4j.openpackaging.parts.WordprocessingML.CommentsPart; import org.docx4j.wml.*; import org.docx4j.wml.Comments.Comment; -import pro.verron.docxstamper.api.DocxStamperException; +import pro.verron.docxstamper.api.OpcStamperException; import java.math.BigInteger; import java.security.MessageDigest; @@ -56,7 +56,7 @@ private static MessageDigest findDigest() { try { return MessageDigest.getInstance("SHA-1"); } catch (NoSuchAlgorithmException e) { - throw new DocxStamperException(e); + throw new OpcStamperException(e); } } diff --git a/src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java b/src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java index 01e5491e..a7324b5d 100644 --- a/src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java +++ b/src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java @@ -9,9 +9,9 @@ import org.docx4j.openpackaging.parts.relationships.RelationshipsPart; import org.docx4j.relationships.Relationship; import org.docx4j.wml.P; -import pro.verron.docxstamper.api.DocxStamperConfiguration; import pro.verron.docxstamper.api.LoadingOpcStamper; -import pro.verron.docxstamper.preset.Stampers; +import pro.verron.docxstamper.api.OpcStamperConfiguration; +import pro.verron.docxstamper.preset.OpcStampers; import java.io.IOException; import java.io.InputStream; @@ -38,10 +38,10 @@ public final class TestDocxStamper { /** *

Constructor for TestDocxStamper.

* - * @param config a {@link DocxStamperConfiguration} object + * @param config a {@link OpcStamperConfiguration} object * @since 1.6.6 */ - public TestDocxStamper(DocxStamperConfiguration config) { + public TestDocxStamper(OpcStamperConfiguration config) { Function loader = inputStream -> { try { return WordprocessingMLPackage.load(inputStream); @@ -49,7 +49,8 @@ public TestDocxStamper(DocxStamperConfiguration config) { throw new RuntimeException(e); } }; - stamper = new LoadingOpcStamper<>(loader, Stampers.from(config)); + stamper = new LoadingOpcStamper<>(loader, + OpcStampers.docxStamper(config)); } /** From ed04ba359be3b9a6b51150c69825b3b7c616c713 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 17 Mar 2024 03:29:43 +0100 Subject: [PATCH 046/134] Update version in pom.xml to 1.6.8 This commit shows the updated version of the docx-stamper project, advancing from 1.6.7 to 1.6.8. This version change is in preparation for the upcoming release and will be packaged as 'jar'. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 01d7e379..88b266bd 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 pro.verron docx-stamper - 1.6.7 + 1.6.8 jar docx-stamper From 8dad4574b2ddbc6eedf5373e376b3a19efb3c8e9 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Tue, 19 Mar 2024 13:21:57 +0100 Subject: [PATCH 047/134] Rename module and class from 'opcstamper' to 'officestamper' The commit includes changes to rename 'OpcStampers' to 'OfficeStampers'. These changes are reflected across several files, including class imports, module declarations, and references within comments and methods. This renaming better reflects the function of the class and aligns with project naming conventions. --- src/main/java/module-info.java | 2 +- src/main/java/org/wickedsource/docxstamper/DocxStamper.java | 6 +++--- src/main/java/pro/verron/docxstamper/StamperFactory.java | 4 ++-- .../preset/{OpcStampers.java => OfficeStampers.java} | 2 +- src/test/java/module-info.java | 4 ++-- .../java/pro/verron/docxstamper/test/TestDocxStamper.java | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) rename src/main/java/pro/verron/docxstamper/preset/{OpcStampers.java => OfficeStampers.java} (97%) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 7b7c59ae..011459c1 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,4 +1,4 @@ -module pro.verron.opcstamper { +module pro.verron.officestamper { requires spring.core; requires spring.expression; diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java index 8afd5582..6541afe5 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java @@ -14,8 +14,8 @@ import pro.verron.docxstamper.core.ObjectResolverRegistry; import pro.verron.docxstamper.core.PlaceholderReplacer; import pro.verron.docxstamper.core.Placeholders; +import pro.verron.docxstamper.preset.OfficeStampers; import pro.verron.docxstamper.preset.OpcStamperConfigurations; -import pro.verron.docxstamper.preset.OpcStampers; import java.io.InputStream; import java.io.OutputStream; @@ -36,7 +36,7 @@ * of the library modularization, because it * exposes too many implementation details to library users, and makes it * hard to extend the library comfortably. - * It is recommended to use the {@link OpcStampers} methods instead. + * It is recommended to use the {@link OfficeStampers} methods instead. * This class will not be exported in the future releases of the module. */ @Deprecated(since = "1.6.8", forRemoval = true) @@ -49,7 +49,7 @@ public class DocxStamper /** * Creates a new DocxStamper with the default configuration. * - * @deprecated since 1.6.4, use {@link OpcStampers#docxStamper()} or {@link OpcStampers#nopreprocessingDocxStamper()} instead. + * @deprecated since 1.6.4, use {@link OfficeStampers#docxStamper()} or {@link OfficeStampers#nopreprocessingDocxStamper()} instead. */ @Deprecated(since = "1.6.4", forRemoval = true) public DocxStamper() { diff --git a/src/main/java/pro/verron/docxstamper/StamperFactory.java b/src/main/java/pro/verron/docxstamper/StamperFactory.java index e4a1bf9d..47669631 100644 --- a/src/main/java/pro/verron/docxstamper/StamperFactory.java +++ b/src/main/java/pro/verron/docxstamper/StamperFactory.java @@ -1,6 +1,6 @@ package pro.verron.docxstamper; -import pro.verron.docxstamper.preset.OpcStampers; +import pro.verron.docxstamper.preset.OfficeStampers; /** * @deprecated since 1.6.8, This class has been deprecated in the effort @@ -11,5 +11,5 @@ */ @Deprecated(since = "1.6.8", forRemoval = true) public class StamperFactory - extends OpcStampers { + extends OfficeStampers { } diff --git a/src/main/java/pro/verron/docxstamper/preset/OpcStampers.java b/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java similarity index 97% rename from src/main/java/pro/verron/docxstamper/preset/OpcStampers.java rename to src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java index 7b264efd..cc18e5fd 100644 --- a/src/main/java/pro/verron/docxstamper/preset/OpcStampers.java +++ b/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java @@ -17,7 +17,7 @@ * @version ${version} * @since 1.6.4 */ -public class OpcStampers { +public class OfficeStampers { public static OpcStamper docxStamper( OpcStamperConfiguration config diff --git a/src/test/java/module-info.java b/src/test/java/module-info.java index 5cdc084e..18369369 100644 --- a/src/test/java/module-info.java +++ b/src/test/java/module-info.java @@ -1,5 +1,5 @@ -module pro.verron.opcstamper.test { - requires transitive pro.verron.opcstamper; +module pro.verron.officestamper.test { + requires transitive pro.verron.officestamper; requires org.junit.jupiter.api; requires org.junit.jupiter.params; diff --git a/src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java b/src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java index a7324b5d..2d045ab0 100644 --- a/src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java +++ b/src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java @@ -11,7 +11,7 @@ import org.docx4j.wml.P; import pro.verron.docxstamper.api.LoadingOpcStamper; import pro.verron.docxstamper.api.OpcStamperConfiguration; -import pro.verron.docxstamper.preset.OpcStampers; +import pro.verron.docxstamper.preset.OfficeStampers; import java.io.IOException; import java.io.InputStream; @@ -50,7 +50,7 @@ public TestDocxStamper(OpcStamperConfiguration config) { } }; stamper = new LoadingOpcStamper<>(loader, - OpcStampers.docxStamper(config)); + OfficeStampers.docxStamper(config)); } /** From 37f77b97791731e567d5a05d01a8f041039153eb Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Tue, 19 Mar 2024 13:51:20 +0100 Subject: [PATCH 048/134] Rename OpcStamper to OfficeStamper across codebase The commit updates several classes and methods, transitioning usage of 'OpcStamper' to 'OfficeStamper'. This change provides a naming scheme that is clearer and more in line with the purpose and functionality of the referenced elements. Changes include file renames, method adjustments, and comment alterations for consistent terminology. --- .../wickedsource/docxstamper/DocxStamper.java | 14 +-- .../docxstamper/DocxStamperConfiguration.java | 10 +-- .../docxstamper/api/DocxStamperException.java | 6 +- .../repeat/RepeatDocPartProcessor.java | 12 +-- ...Stamper.java => LoadingOfficeStamper.java} | 14 +-- .../{OpcStamper.java => OfficeStamper.java} | 8 +- ...n.java => OfficeStamperConfiguration.java} | 34 +++---- .../api/OfficeStamperException.java | 13 +++ .../docxstamper/api/OpcStamperException.java | 13 --- .../docxstamper/core/PlaceholderReplacer.java | 4 +- .../preset/CommentProcessorFactory.java | 10 +-- ....java => OfficeStamperConfigurations.java} | 8 +- .../docxstamper/preset/OfficeStampers.java | 12 +-- .../verron/docxstamper/test/DefaultTests.java | 90 +++++++++---------- .../test/FailOnUnresolvedPlaceholderTest.java | 10 +-- .../docxstamper/test/MultiSectionTest.java | 4 +- .../docxstamper/test/MultiStampTest.java | 4 +- .../test/NullPointerResolutionTest.java | 8 +- ...olderReplacementInHeaderAndFooterTest.java | 4 +- ...PlaceholderReplacementInTextBoxesTest.java | 4 +- .../test/RepeatDocPartBadPlaceholderTest.java | 8 +- .../docxstamper/test/SpelInjectionTest.java | 8 +- .../docxstamper/test/StampTableTest.java | 4 +- .../verron/docxstamper/test/Stringifier.java | 4 +- .../docxstamper/test/TestDocxStamper.java | 14 +-- 25 files changed, 161 insertions(+), 159 deletions(-) rename src/main/java/pro/verron/docxstamper/api/{LoadingOpcStamper.java => LoadingOfficeStamper.java} (76%) rename src/main/java/pro/verron/docxstamper/api/{OpcStamper.java => OfficeStamper.java} (76%) rename src/main/java/pro/verron/docxstamper/api/{OpcStamperConfiguration.java => OfficeStamperConfiguration.java} (69%) create mode 100644 src/main/java/pro/verron/docxstamper/api/OfficeStamperException.java delete mode 100644 src/main/java/pro/verron/docxstamper/api/OpcStamperException.java rename src/main/java/pro/verron/docxstamper/preset/{OpcStamperConfigurations.java => OfficeStamperConfigurations.java} (66%) diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java index 6541afe5..e2aa1d14 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java @@ -14,8 +14,8 @@ import pro.verron.docxstamper.core.ObjectResolverRegistry; import pro.verron.docxstamper.core.PlaceholderReplacer; import pro.verron.docxstamper.core.Placeholders; +import pro.verron.docxstamper.preset.OfficeStamperConfigurations; import pro.verron.docxstamper.preset.OfficeStampers; -import pro.verron.docxstamper.preset.OpcStamperConfigurations; import java.io.InputStream; import java.io.OutputStream; @@ -25,7 +25,9 @@ import java.util.function.Function; /** - * The DocxStamper class is an implementation of the OpcStamper interface that is used to stamp DOCX templates with a context object and write the result to an output stream. + * The DocxStamper class is an implementation of the {@link OfficeStamper} + * interface that is used to stamp DOCX templates with a context object and + * write the result to an output stream. * * @param The type of the context that can be stamped * @author Tom Hombergs @@ -41,7 +43,7 @@ */ @Deprecated(since = "1.6.8", forRemoval = true) public class DocxStamper - implements OpcStamper { + implements OfficeStamper { private final List preprocessors; private final PlaceholderReplacer placeholderReplacer; private final CommentProcessorRegistry commentProcessorRegistry; @@ -53,7 +55,7 @@ public class DocxStamper */ @Deprecated(since = "1.6.4", forRemoval = true) public DocxStamper() { - this(OpcStamperConfigurations.standard()); + this(OfficeStamperConfigurations.standard()); } /** @@ -61,7 +63,7 @@ public DocxStamper() { * * @param configuration the configuration to use for this DocxStamper. */ - public DocxStamper(OpcStamperConfiguration configuration) { + public DocxStamper(OfficeStamperConfiguration configuration) { this( configuration.isFailOnUnresolvedExpression(), configuration.isReplaceUnresolvedExpressions(), @@ -174,7 +176,7 @@ private static TypedValue throwException(ReflectiveOperationException exception) * *

* If you need a wider vocabulary of methods available in the comments, you can create your own ICommentProcessor - * and register it via {@link OpcStamperConfiguration#addCommentProcessor(Class, Function)}. + * and register it via {@link OfficeStamperConfiguration#addCommentProcessor(Class, Function)}. *

*/ public void stamp( diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java index ceba4877..b3b1c788 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java @@ -15,7 +15,7 @@ import org.wickedsource.docxstamper.replace.typeresolver.TypeResolver; import pro.verron.docxstamper.api.*; import pro.verron.docxstamper.preset.CommentProcessorFactory; -import pro.verron.docxstamper.preset.OpcStamperConfigurations; +import pro.verron.docxstamper.preset.OfficeStamperConfigurations; import pro.verron.docxstamper.preset.Resolvers; import pro.verron.docxstamper.preset.resolver.Null2DefaultResolver; @@ -37,13 +37,13 @@ * of the library modularization, because it * exposes too many implementation details to library users, and makes it * hard to extend the library comfortably. - * It is recommended to use the {@link OpcStamperConfigurations#standard()} method and - * {@link OpcStamperConfiguration} interface instead. + * It is recommended to use the {@link OfficeStamperConfigurations#standard()} method and + * {@link OfficeStamperConfiguration} interface instead. * This class will not be exported in the future releases of the module. */ @Deprecated(since = "1.6.8", forRemoval = true) public class DocxStamperConfiguration - implements OpcStamperConfiguration { + implements OfficeStamperConfiguration { private final Map, Function> commentProcessors = new HashMap<>(); private final List resolvers = new ArrayList<>(); @@ -274,7 +274,7 @@ public DocxStamperConfiguration addCommentProcessor( */ @Override @Deprecated(forRemoval = true, since = "1.6.4") - public OpcStamper build() { + public OfficeStamper build() { return new DocxStamper<>(this); } diff --git a/src/main/java/org/wickedsource/docxstamper/api/DocxStamperException.java b/src/main/java/org/wickedsource/docxstamper/api/DocxStamperException.java index 37d6381c..dd486c0b 100644 --- a/src/main/java/org/wickedsource/docxstamper/api/DocxStamperException.java +++ b/src/main/java/org/wickedsource/docxstamper/api/DocxStamperException.java @@ -1,6 +1,6 @@ package org.wickedsource.docxstamper.api; -import pro.verron.docxstamper.api.OpcStamperException; +import pro.verron.docxstamper.api.OfficeStamperException; /** * This class represents an exception that can be thrown during the processing of a Docx file using the DocxStamper library. @@ -12,12 +12,12 @@ * @since 1.0.0 * @deprecated since 1.6.8, This class has been deprecated in the effort * of the library modularization. - * It is recommended to use the {@link OpcStamperException} class instead. + * It is recommended to use the {@link OfficeStamperException} class instead. * This class will not be exported in the future releases of the module. */ @Deprecated(since = "1.6.8", forRemoval = true) public class DocxStamperException - extends OpcStamperException { + extends OfficeStamperException { /** *

Constructor for DocxStamperException.

diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java index 368ed6fa..1b83a2fa 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java @@ -12,7 +12,7 @@ import org.wickedsource.docxstamper.util.SectionUtil; import pro.verron.docxstamper.api.Comment; import pro.verron.docxstamper.api.CommentProcessor; -import pro.verron.docxstamper.api.OpcStamper; +import pro.verron.docxstamper.api.OfficeStamper; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.core.PlaceholderReplacer; @@ -35,7 +35,7 @@ /** * This class is responsible for processing the <ds: repeat> tag. - * It uses the {@link OpcStamper} to stamp the sub document and then + * It uses the {@link OfficeStamper} to stamp the sub document and then * copies the resulting sub document to the correct position in the * main document. * @@ -50,13 +50,13 @@ public class RepeatDocPartProcessor private static final ThreadFactory threadFactory = Executors.defaultThreadFactory(); private static final ObjectFactory objectFactory = Context.getWmlObjectFactory(); - private final OpcStamper stamper; + private final OfficeStamper stamper; private final Map> contexts = new HashMap<>(); private final Supplier> nullSupplier; private RepeatDocPartProcessor( ParagraphPlaceholderReplacer placeholderReplacer, - OpcStamper stamper, + OfficeStamper stamper, Supplier> nullSupplier ) { super(placeholderReplacer); @@ -75,7 +75,7 @@ private RepeatDocPartProcessor( */ public static CommentProcessor newInstance( PlaceholderReplacer pr, - OpcStamper stamper, + OfficeStamper stamper, String nullReplacementValue ) { Supplier> nullSupplier = () -> singletonList( @@ -92,7 +92,7 @@ public static CommentProcessor newInstance( */ public static CommentProcessor newInstance( ParagraphPlaceholderReplacer pr, - OpcStamper stamper + OfficeStamper stamper ) { return new RepeatDocPartProcessor(pr, stamper, Collections::emptyList); } diff --git a/src/main/java/pro/verron/docxstamper/api/LoadingOpcStamper.java b/src/main/java/pro/verron/docxstamper/api/LoadingOfficeStamper.java similarity index 76% rename from src/main/java/pro/verron/docxstamper/api/LoadingOpcStamper.java rename to src/main/java/pro/verron/docxstamper/api/LoadingOfficeStamper.java index 6754c7d3..33460463 100644 --- a/src/main/java/pro/verron/docxstamper/api/LoadingOpcStamper.java +++ b/src/main/java/pro/verron/docxstamper/api/LoadingOfficeStamper.java @@ -7,7 +7,7 @@ import java.util.function.Function; /** - * OpcStamper is an interface that defines the contract for stamping + * This is an interface that defines the contract for stamping * templates with context and writing the result to an {@link OutputStream}. * * @param The type of the template that can be stamped. @@ -15,14 +15,14 @@ * @version ${version} * @since 1.6.4 */ -public class LoadingOpcStamper { +public class LoadingOfficeStamper { private final Function loader; - private final OpcStamper stamper; + private final OfficeStamper stamper; - public LoadingOpcStamper( + public LoadingOfficeStamper( Function loader, - OpcStamper stamper + OfficeStamper stamper ) { this.loader = loader; this.stamper = stamper; @@ -34,13 +34,13 @@ public LoadingOpcStamper( * @param inputStream template to stamp * @param context context to use for stamping * @param outputStream output stream to write the result to - * @throws OpcStamperException if the stamping fails + * @throws OfficeStamperException if the stamping fails */ public void stamp( InputStream inputStream, Object context, OutputStream outputStream - ) throws OpcStamperException { + ) throws OfficeStamperException { T mlPackage = loader.apply(inputStream); stamper.stamp(mlPackage, context, outputStream); } diff --git a/src/main/java/pro/verron/docxstamper/api/OpcStamper.java b/src/main/java/pro/verron/docxstamper/api/OfficeStamper.java similarity index 76% rename from src/main/java/pro/verron/docxstamper/api/OpcStamper.java rename to src/main/java/pro/verron/docxstamper/api/OfficeStamper.java index 8083c19f..47746d50 100644 --- a/src/main/java/pro/verron/docxstamper/api/OpcStamper.java +++ b/src/main/java/pro/verron/docxstamper/api/OfficeStamper.java @@ -5,7 +5,7 @@ import java.io.OutputStream; /** - * OpcStamper is an interface that defines the contract for stamping + * This is an interface that defines the contract for stamping * templates with context and writing the result to an {@link OutputStream}. * * @param The type of the template that can be stamped. @@ -13,18 +13,18 @@ * @version ${version} * @since 1.6.4 */ -public interface OpcStamper { +public interface OfficeStamper { /** * Stamps the template with the context and writes the result to the outputStream. * * @param template template to stamp * @param context context to use for stamping * @param outputStream output stream to write the result to - * @throws OpcStamperException if the stamping fails + * @throws OfficeStamperException if the stamping fails */ void stamp( T template, Object context, OutputStream outputStream - ) throws OpcStamperException; + ) throws OfficeStamperException; } diff --git a/src/main/java/pro/verron/docxstamper/api/OpcStamperConfiguration.java b/src/main/java/pro/verron/docxstamper/api/OfficeStamperConfiguration.java similarity index 69% rename from src/main/java/pro/verron/docxstamper/api/OpcStamperConfiguration.java rename to src/main/java/pro/verron/docxstamper/api/OfficeStamperConfiguration.java index c756eaa2..582bd165 100644 --- a/src/main/java/pro/verron/docxstamper/api/OpcStamperConfiguration.java +++ b/src/main/java/pro/verron/docxstamper/api/OfficeStamperConfiguration.java @@ -9,46 +9,46 @@ import java.util.Optional; import java.util.function.Function; -public interface OpcStamperConfiguration { +public interface OfficeStamperConfiguration { @Deprecated(since = "1.6.7") Optional nullReplacementValue(); boolean isFailOnUnresolvedExpression(); - OpcStamperConfiguration setFailOnUnresolvedExpression(boolean failOnUnresolvedExpression); + OfficeStamperConfiguration setFailOnUnresolvedExpression(boolean failOnUnresolvedExpression); @Deprecated(since = "1.6.7", forRemoval = true) - OpcStamperConfiguration nullValuesDefault(String nullValuesDefault); + OfficeStamperConfiguration nullValuesDefault(String nullValuesDefault); @Deprecated(since = "1.6.7", forRemoval = true) - OpcStamperConfiguration replaceNullValues(boolean replaceNullValues); + OfficeStamperConfiguration replaceNullValues(boolean replaceNullValues); - OpcStamperConfiguration unresolvedExpressionsDefaultValue(String unresolvedExpressionsDefaultValue); + OfficeStamperConfiguration unresolvedExpressionsDefaultValue(String unresolvedExpressionsDefaultValue); - OpcStamperConfiguration replaceUnresolvedExpressions( + OfficeStamperConfiguration replaceUnresolvedExpressions( boolean replaceUnresolvedExpressions ); - OpcStamperConfiguration leaveEmptyOnExpressionError( + OfficeStamperConfiguration leaveEmptyOnExpressionError( boolean leaveEmpty ); @Deprecated(since = "1.6.7", forRemoval = true) - OpcStamperConfiguration addTypeResolver( + OfficeStamperConfiguration addTypeResolver( Class resolvedType, ITypeResolver resolver ); - OpcStamperConfiguration exposeInterfaceToExpressionLanguage( + OfficeStamperConfiguration exposeInterfaceToExpressionLanguage( Class interfaceClass, Object implementation ); - OpcStamperConfiguration addCommentProcessor( + OfficeStamperConfiguration addCommentProcessor( Class interfaceClass, Function commentProcessorFactory ); @Deprecated(forRemoval = true, since = "1.6.4") - OpcStamper build(); + OfficeStamper build(); void addPreprocessor(PreProcessor preprocessor); @@ -60,19 +60,19 @@ OpcStamperConfiguration addCommentProcessor( String getLineBreakPlaceholder(); - OpcStamperConfiguration setLineBreakPlaceholder( + OfficeStamperConfiguration setLineBreakPlaceholder( @NonNull String lineBreakPlaceholder ); EvaluationContextConfigurer getEvaluationContextConfigurer(); - OpcStamperConfiguration setEvaluationContextConfigurer( + OfficeStamperConfiguration setEvaluationContextConfigurer( EvaluationContextConfigurer evaluationContextConfigurer ); SpelParserConfiguration getSpelParserConfiguration(); - OpcStamperConfiguration setSpelParserConfiguration( + OfficeStamperConfiguration setSpelParserConfiguration( SpelParserConfiguration spelParserConfiguration ); @@ -85,7 +85,7 @@ OpcStamperConfiguration setSpelParserConfiguration( ITypeResolver getDefaultTypeResolver(); @Deprecated(since = "1.6.7", forRemoval = true) - OpcStamperConfiguration setDefaultTypeResolver( + OfficeStamperConfiguration setDefaultTypeResolver( ITypeResolver defaultResolver ); @@ -101,11 +101,11 @@ OpcStamperConfiguration setDefaultTypeResolver( List getResolvers(); - OpcStamperConfiguration setResolvers( + OfficeStamperConfiguration setResolvers( List resolvers ); - OpcStamperConfiguration addResolver( + OfficeStamperConfiguration addResolver( ObjectResolver resolver ); } diff --git a/src/main/java/pro/verron/docxstamper/api/OfficeStamperException.java b/src/main/java/pro/verron/docxstamper/api/OfficeStamperException.java new file mode 100644 index 00000000..d075855c --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/api/OfficeStamperException.java @@ -0,0 +1,13 @@ +package pro.verron.docxstamper.api; + +public class OfficeStamperException + extends RuntimeException { + public OfficeStamperException(String message) {super(message);} + + public OfficeStamperException(Throwable cause) {super(cause);} + + public OfficeStamperException(String message, Throwable cause) { + super(message, + cause); + } +} diff --git a/src/main/java/pro/verron/docxstamper/api/OpcStamperException.java b/src/main/java/pro/verron/docxstamper/api/OpcStamperException.java deleted file mode 100644 index 3b12c0be..00000000 --- a/src/main/java/pro/verron/docxstamper/api/OpcStamperException.java +++ /dev/null @@ -1,13 +0,0 @@ -package pro.verron.docxstamper.api; - -public class OpcStamperException - extends RuntimeException { - public OpcStamperException(String message) {super(message);} - - public OpcStamperException(Throwable cause) {super(cause);} - - public OpcStamperException(String message, Throwable cause) { - super(message, - cause); - } -} diff --git a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java index e28e8673..0a24464c 100644 --- a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java +++ b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java @@ -12,7 +12,7 @@ import org.wickedsource.docxstamper.el.ExpressionResolver; import org.wickedsource.docxstamper.util.RunUtil; import org.wickedsource.docxstamper.util.walk.BaseCoordinatesWalker; -import pro.verron.docxstamper.api.OpcStamperException; +import pro.verron.docxstamper.api.OfficeStamperException; import pro.verron.docxstamper.api.Paragraph; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.api.Placeholder; @@ -119,7 +119,7 @@ public void resolveExpressionsForParagraph( if (failOnUnresolvedExpression) { String message = "Expression %s could not be resolved against context of type %s" .formatted(expression, context.getClass()); - throw new OpcStamperException(message, e); + throw new OfficeStamperException(message, e); } else if (leaveEmptyOnExpressionError) { log.warn( "Expression {} could not be resolved against context root of type {}. Reason: {}. Set log level to TRACE to view Stacktrace.", diff --git a/src/main/java/pro/verron/docxstamper/preset/CommentProcessorFactory.java b/src/main/java/pro/verron/docxstamper/preset/CommentProcessorFactory.java index d9bc98de..0f4b2ee4 100644 --- a/src/main/java/pro/verron/docxstamper/preset/CommentProcessorFactory.java +++ b/src/main/java/pro/verron/docxstamper/preset/CommentProcessorFactory.java @@ -10,8 +10,8 @@ import org.wickedsource.docxstamper.processor.replaceExpression.ReplaceWithProcessor; import org.wickedsource.docxstamper.processor.table.TableResolver; import pro.verron.docxstamper.api.CommentProcessor; -import pro.verron.docxstamper.api.OpcStamper; -import pro.verron.docxstamper.api.OpcStamperConfiguration; +import pro.verron.docxstamper.api.OfficeStamper; +import pro.verron.docxstamper.api.OfficeStamperConfiguration; import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; import pro.verron.docxstamper.core.PlaceholderReplacer; @@ -23,14 +23,14 @@ * @since 1.6.4 */ public class CommentProcessorFactory { - private final OpcStamperConfiguration configuration; + private final OfficeStamperConfiguration configuration; /** * Creates a new CommentProcessorFactory. * * @param configuration the configuration to use for the created processors. */ - public CommentProcessorFactory(OpcStamperConfiguration configuration) { + public CommentProcessorFactory(OfficeStamperConfiguration configuration) { this.configuration = configuration; } @@ -54,7 +54,7 @@ public CommentProcessor repeatDocPart(ParagraphPlaceholderReplacer pr) { return RepeatDocPartProcessor.newInstance(pr, getStamper()); } - private OpcStamper getStamper() { + private OfficeStamper getStamper() { return (template, context, output) -> new DocxStamper<>(configuration).stamp(template, context, output); } diff --git a/src/main/java/pro/verron/docxstamper/preset/OpcStamperConfigurations.java b/src/main/java/pro/verron/docxstamper/preset/OfficeStamperConfigurations.java similarity index 66% rename from src/main/java/pro/verron/docxstamper/preset/OpcStamperConfigurations.java rename to src/main/java/pro/verron/docxstamper/preset/OfficeStamperConfigurations.java index d7229969..0db663e8 100644 --- a/src/main/java/pro/verron/docxstamper/preset/OpcStamperConfigurations.java +++ b/src/main/java/pro/verron/docxstamper/preset/OfficeStamperConfigurations.java @@ -2,14 +2,14 @@ import org.wickedsource.docxstamper.preprocessor.MergeSameStyleRuns; import org.wickedsource.docxstamper.preprocessor.RemoveProofErrors; -import pro.verron.docxstamper.api.OpcStamperConfiguration; +import pro.verron.docxstamper.api.OfficeStamperConfiguration; -public class OpcStamperConfigurations { - public static OpcStamperConfiguration standard() { +public class OfficeStamperConfigurations { + public static OfficeStamperConfiguration standard() { return new org.wickedsource.docxstamper.DocxStamperConfiguration(); } - static OpcStamperConfiguration standardWithPreprocessing() { + static OfficeStamperConfiguration standardWithPreprocessing() { var configuration = standard(); configuration.addPreprocessor(new RemoveProofErrors()); configuration.addPreprocessor(new MergeSameStyleRuns()); diff --git a/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java b/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java index cc18e5fd..cb02e301 100644 --- a/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java +++ b/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java @@ -4,8 +4,8 @@ import org.wickedsource.docxstamper.DocxStamper; import org.wickedsource.docxstamper.preprocessor.MergeSameStyleRuns; import org.wickedsource.docxstamper.preprocessor.RemoveProofErrors; -import pro.verron.docxstamper.api.OpcStamper; -import pro.verron.docxstamper.api.OpcStamperConfiguration; +import pro.verron.docxstamper.api.OfficeStamper; +import pro.verron.docxstamper.api.OfficeStamperConfiguration; /** * Main class of the docx-stamper library. @@ -19,8 +19,8 @@ */ public class OfficeStampers { - public static OpcStamper docxStamper( - OpcStamperConfiguration config + public static OfficeStamper docxStamper( + OfficeStamperConfiguration config ) { return new DocxStamper<>(config); } @@ -31,8 +31,8 @@ public static OpcStamper docxStamper( * * @return a new DocxStamper */ - public OpcStamper docxStamper() { - return new DocxStamper<>(OpcStamperConfigurations.standardWithPreprocessing()); + public OfficeStamper docxStamper() { + return new DocxStamper<>(OfficeStamperConfigurations.standardWithPreprocessing()); } } diff --git a/src/test/java/pro/verron/docxstamper/test/DefaultTests.java b/src/test/java/pro/verron/docxstamper/test/DefaultTests.java index a6c8745d..c62b6a39 100644 --- a/src/test/java/pro/verron/docxstamper/test/DefaultTests.java +++ b/src/test/java/pro/verron/docxstamper/test/DefaultTests.java @@ -7,9 +7,9 @@ import org.springframework.context.expression.MapAccessor; import org.springframework.expression.spel.SpelParserConfiguration; import pro.verron.docxstamper.api.Image; -import pro.verron.docxstamper.api.OpcStamperConfiguration; +import pro.verron.docxstamper.api.OfficeStamperConfiguration; import pro.verron.docxstamper.preset.EvaluationContextConfigurers; -import pro.verron.docxstamper.preset.OpcStamperConfigurations; +import pro.verron.docxstamper.preset.OfficeStamperConfigurations; import pro.verron.docxstamper.preset.Resolvers; import java.io.IOException; @@ -54,7 +54,7 @@ public static InputStream getResource(Path path) { private static Arguments replaceWordWithIntegrationTest() { return of("replaceWordWithIntegrationTest", - OpcStamperConfigurations.standard(), + OfficeStamperConfigurations.standard(), name("Simpsons"), getResource(Path.of("integration", "ReplaceWordWithIntegrationTest.docx")), @@ -67,7 +67,7 @@ private static Arguments replaceWordWithIntegrationTest() { private static Arguments repeatingRows() { return of("Repeating table rows should be possible", - OpcStamperConfigurations.standard(), + OfficeStamperConfigurations.standard(), roles(role("Homer Simpson", "Dan Castellaneta"), role("Marge Simpson", "Julie Kavner"), role("Bart Simpson", "Nancy Cartwright"), @@ -98,7 +98,7 @@ private static Arguments repeatingRows() { private static Arguments ternary() { return of("Ternary operators should function", - OpcStamperConfigurations.standard(), + OfficeStamperConfigurations.standard(), name("Homer"), getResource(Path.of("TernaryOperatorTest.docx")), """ @@ -111,7 +111,7 @@ private static Arguments ternary() { private static Arguments whitespaces() { return of("White spaces should be preserved", - OpcStamperConfigurations.standard(), + OfficeStamperConfigurations.standard(), name("Homer Simpson"), getResource(Path.of("TabsIndentationTest.docx")), """ @@ -121,7 +121,7 @@ private static Arguments whitespaces() { private static Arguments tabulations() { return of("Tabulation should be preserved", - OpcStamperConfigurations.standard(), + OfficeStamperConfigurations.standard(), name("Homer Simpson"), getResource(Path.of("TabsIndentationTest.docx")), """ @@ -131,7 +131,7 @@ private static Arguments tabulations() { private static Arguments replaceNullExpressionTest() { return of("Do not replace 'null' values", - OpcStamperConfigurations.standard() + OfficeStamperConfigurations.standard() .addResolver(Resolvers.nullToPlaceholder()), name(null), getResource(Path.of("ReplaceNullExpressionTest.docx")), @@ -140,7 +140,7 @@ private static Arguments replaceNullExpressionTest() { private static Arguments replaceNullExpressionTest2() { return of("Do replace 'null' values", - OpcStamperConfigurations.standard() + OfficeStamperConfigurations.standard() .addResolver(Resolvers.nullToEmpty()), name(null), getResource(Path.of("ReplaceNullExpressionTest.docx")), @@ -149,7 +149,7 @@ private static Arguments replaceNullExpressionTest2() { private static Arguments repeatTableRowKeepsFormatTest() { return of("repeatTableRowKeepsFormatTest", - OpcStamperConfigurations.standard(), + OfficeStamperConfigurations.standard(), new Show(List.of(new CharacterRecord(1, "st", "Homer Simpson", @@ -209,7 +209,7 @@ private static Arguments repeatParagraphTest() { ❬There are ❬6❘lang=de-DE❭ characters.❘spacing={after=140,before=0}❭"""; return arguments("repeatParagraphTest", - OpcStamperConfigurations.standard(), + OfficeStamperConfigurations.standard(), context, template, expected); @@ -234,7 +234,7 @@ private static Arguments repeatDocPartWithImageTestShouldImportImageDataInTheMai rId13:image/png:193.6kB:sha1=t8UNAmo7yJgZJk9g7pLLIb3AvCA=:cy=$d:6120130 """; - var config = OpcStamperConfigurations.standard() + var config = OfficeStamperConfigurations.standard() .setEvaluationContextConfigurer( ctx -> ctx.addPropertyAccessor(new MapAccessor())); return of( @@ -256,7 +256,7 @@ private static Image getImage(Path path) { private static Arguments repeatDocPartWithImagesInSourceTestshouldReplicateImageFromTheMainDocumentInTheSubTemplate() { return of( "repeatDocPartWithImagesInSourceTestshouldReplicateImageFromTheMainDocumentInTheSubTemplate", - OpcStamperConfigurations.standard() + OfficeStamperConfigurations.standard() .setEvaluationContextConfigurer( (ctx) -> ctx.addPropertyAccessor(new MapAccessor())), Contexts.subDocPartContext(), @@ -275,7 +275,7 @@ private static Arguments repeatDocPartWithImagesInSourceTestshouldReplicateImage private static Arguments repeatDocPartTest() { return of("repeatDocPartTest", - OpcStamperConfigurations.standard(), + OfficeStamperConfigurations.standard(), new Characters(List.of( new Role("Homer Simpson", "Dan Castellaneta"), new Role("Marge Simpson", "Julie Kavner"), @@ -316,7 +316,7 @@ private static Arguments repeatDocPartTest() { private static Arguments repeatDocPartNestingTest() { return of("repeatDocPartNestingTest", - OpcStamperConfigurations.standard(), + OfficeStamperConfigurations.standard(), Contexts.schoolContext(), getResource(Path.of("RepeatDocPartNestingTest.docx")), """ @@ -503,7 +503,7 @@ private static Arguments repeatDocPartAndCommentProcessorsIsolationTest_repeatDo This will stay untouched too."""; - var config = OpcStamperConfigurations.standard() + var config = OfficeStamperConfigurations.standard() .setEvaluationContextConfigurer( (ctx) -> ctx.addPropertyAccessor(new MapAccessor())); @@ -518,7 +518,7 @@ private static Arguments repeatDocPartAndCommentProcessorsIsolationTest_repeatDo private static Arguments changingPageLayoutTest_shouldKeepSectionBreakOrientationInRepeatParagraphWithoutSectionBreakInsideComment() { return arguments( "changingPageLayoutTest_shouldKeepSectionBreakOrientationInRepeatParagraphWithoutSectionBreakInsideComment", - OpcStamperConfigurations.standard() + OfficeStamperConfigurations.standard() .setEvaluationContextConfigurer( ctx -> ctx.addPropertyAccessor(new MapAccessor())), Map.of("repeatValues", @@ -558,7 +558,7 @@ Second page is portrait, layout change should survive to repeatParagraph process ❬❘docGrid=xxx,eGHdrFtrReferences=xxx,pgMar=xxx,pgSz={h=11906,orient=landscape,w=16838}❭ Fourth page is set to portrait again."""; - var config = OpcStamperConfigurations.standard() + var config = OfficeStamperConfigurations.standard() .setEvaluationContextConfigurer( ctx -> ctx.addPropertyAccessor(new MapAccessor())); return arguments( @@ -573,7 +573,7 @@ Second page is portrait, layout change should survive to repeatParagraph process private static Arguments changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithSectionBreaksInsideComment() { return arguments( "changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithSectionBreaksInsideComment", - OpcStamperConfigurations.standard() + OfficeStamperConfigurations.standard() .setEvaluationContextConfigurer( ctx -> ctx.addPropertyAccessor(new MapAccessor())), Map.of("repeatValues", @@ -599,7 +599,7 @@ Second page is landscape, layout change should survive to repeatDocPart (Marge). private static Arguments changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithSectionBreaksInsideCommentAndTableAsLastElement() { return arguments( "changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithSectionBreaksInsideCommentAndTableAsLastElement", - OpcStamperConfigurations.standard() + OfficeStamperConfigurations.standard() .setEvaluationContextConfigurer( ctx -> ctx.addPropertyAccessor(new MapAccessor())), Map.of("repeatValues", @@ -630,7 +630,7 @@ Second page is landscape, layout change should survive to repeatDocPart (Marge). private static Arguments changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithoutSectionBreaksInsideComment() { return arguments( "changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithoutSectionBreaksInsideComment", - OpcStamperConfigurations.standard() + OfficeStamperConfigurations.standard() .setEvaluationContextConfigurer( ctx -> ctx.addPropertyAccessor(new MapAccessor())), Map.of("repeatValues", @@ -669,7 +669,7 @@ private static Arguments conditionalDisplayOfParagraphsTest_processorExpressions return arguments( "conditionalDisplayOfParagraphsTest_processorExpressionsInCommentsAreResolved", - OpcStamperConfigurations.standard(), + OfficeStamperConfigurations.standard(), context, template, expected); @@ -693,7 +693,7 @@ private static Arguments conditionalDisplayOfParagraphsTest_inlineProcessorExpre """; return arguments( "conditionalDisplayOfParagraphsTest_inlineProcessorExpressionsAreResolved", - OpcStamperConfigurations.standard(), + OfficeStamperConfigurations.standard(), context, template, expected); @@ -719,7 +719,7 @@ private static Arguments conditionalDisplayOfParagraphsTest_unresolvedInlineProc """; return arguments( "conditionalDisplayOfParagraphsTest_unresolvedInlineProcessorExpressionsAreRemoved", - OpcStamperConfigurations.standard(), + OfficeStamperConfigurations.standard(), context, template, expected); @@ -739,7 +739,7 @@ private static Arguments conditionalDisplayOfTableRowsTest() { ❬❘spacing={after=140,before=0}❭"""; return arguments("conditionalDisplayOfTableRowsTest", - OpcStamperConfigurations.standard(), + OfficeStamperConfigurations.standard(), context, template, expected); @@ -763,7 +763,7 @@ private static Arguments conditionalDisplayOfTableBug32Test() { ❬❬This paragraph stays untouched.❘lang=de-DE❭❘spacing={after=140,before=0}❭"""; return arguments("conditionalDisplayOfTableBug32Test", - OpcStamperConfigurations.standard(), + OfficeStamperConfigurations.standard(), context, template, expected); @@ -787,7 +787,7 @@ private static Arguments conditionalDisplayOfTableTest() { ❬❬This paragraph stays untouched.❘lang=de-DE❭❘lang=de-DE,spacing={after=140,before=0}❭"""; return arguments("conditionalDisplayOfTablesTest", - OpcStamperConfigurations.standard(), + OfficeStamperConfigurations.standard(), context, template, expected); @@ -801,7 +801,7 @@ private static Arguments customEvaluationContextConfigurerTest_customEvaluationC ❬Custom EvaluationContextConfigurer Test❘spacing={after=120,before=240}❭ ❬❬This paragraph stays untouched.❘lang=de-DE❭❘lang=de-DE❭ ❬The variable foo has the value ❬bar❘lang=de-DE❭.❘spacing={after=140,before=0}❭"""; - var config = OpcStamperConfigurations.standard() + var config = OfficeStamperConfigurations.standard() .setEvaluationContextConfigurer( evalContext -> evalContext.addPropertyAccessor(new SimpleGetter( "foo", @@ -823,7 +823,7 @@ private static Arguments customExpressionFunctionTest() { ❬❬This paragraph is untouched.❘lang=de-DE❭❘lang=de-DE❭ ❬❬In this paragraph, a custom expression function is used to uppercase a String: |BR|❘lang=de-DE❭❬HOMER SIMPSON❘b=true,lang=de-DE❭❬.❘lang=de-DE❭❘spacing={after=140,before=0}❭ ❬❬To test that custom functions work together with comment expressions, we toggle visibility of this paragraph with a comment expression.❘lang=de-DE❭❘spacing={after=140,before=0}❭"""; - var config = OpcStamperConfigurations.standard() + var config = OfficeStamperConfigurations.standard() .exposeInterfaceToExpressionLanguage( Functions.UppercaseFunction.class, Functions.upperCase()); @@ -836,7 +836,7 @@ private static Arguments customExpressionFunctionTest() { private static Arguments customTypeResolverTest() { return arguments("customTypeResolverTest", - OpcStamperConfigurations.standard() + OfficeStamperConfigurations.standard() .addResolver(new CustomTypeResolver()), new Context(new CustomType()), getResource(Path.of("CustomTypeResolverTest.docx")), @@ -857,7 +857,7 @@ private static Arguments dateReplacementTest() { Today is: %s""".formatted(formattedDate); return arguments("dateReplacementTest", - OpcStamperConfigurations.standard(), + OfficeStamperConfigurations.standard(), context, template, expected); @@ -872,7 +872,7 @@ private static Arguments expressionReplacementInGlobalParagraphsTest() { ❬❬This paragraph is untouched.❘lang=de-DE❭❘lang=de-DE❭ ❬In this paragraph, the variable ❘lang=de-DE❭❬name❘b=true,lang=de-DE❭ should be resolved to the value ❬Homer Simpson❘lang=de-DE❭. ❬❬In this paragraph, the variable ❘lang=de-DE❭❬foo❘b=true,lang=de-DE❭❬ should not be resolved: ${foo}.❘lang=de-DE❭❘spacing={after=140,before=0}❭"""; - OpcStamperConfiguration config = OpcStamperConfigurations.standard() + OfficeStamperConfiguration config = OfficeStamperConfigurations.standard() .setFailOnUnresolvedExpression(false); return arguments("expressionReplacementInGlobalParagraphsTest", config, @@ -899,7 +899,7 @@ private static Arguments expressionReplacementInTablesTest() { ${foo} ❬❘spacing={after=140,before=0}❭"""; - OpcStamperConfiguration config = OpcStamperConfigurations.standard() + OfficeStamperConfiguration config = OfficeStamperConfigurations.standard() .setFailOnUnresolvedExpression(false); return arguments("expressionReplacementInTablesTest", config, @@ -931,7 +931,7 @@ private static Arguments expressionReplacementWithFormattingTest() { ❬It should be white over darkblue: ❬Homer Simpson❘color=FFFFFF,highlight=darkBlue❭❘b=true❭ ❬It should be with header formatting: ❬Homer Simpson❘rStyle=TitreCar❭❘b=true❭"""; return arguments("expressionReplacementWithFormattingTest", - OpcStamperConfigurations.standard(), + OfficeStamperConfigurations.standard(), context, template, expected); @@ -952,7 +952,7 @@ private static Arguments expressionWithSurroundingSpacesTest() { Before Expression After. ❬Before Expression After.❘spacing={after=140,before=0}❭"""; return arguments("expressionWithSurroundingSpacesTest", - OpcStamperConfigurations.standard(), + OfficeStamperConfigurations.standard(), spacyContext, template, expected); @@ -967,7 +967,7 @@ private static Arguments expressionReplacementWithCommentTest() { This paragraph is untouched. In this paragraph, the variable ❬name❘b=true❭ should be resolved to the value Homer Simpson. ❬In this paragraph, the variable ❬foo❘b=true❭ should not be resolved: unresolvedValueWithCommentreplaceWordWith(foo).❘spacing={after=140,before=0,line=288,lineRule=AUTO}❭"""; - var config = OpcStamperConfigurations.standard() + var config = OfficeStamperConfigurations.standard() .setFailOnUnresolvedExpression(false); return arguments("expressionReplacementWithCommentsTest", config, @@ -990,7 +990,7 @@ private static Arguments imageReplacementInGlobalParagraphsTest() { ❬In this paragraph, an image of Mona Lisa is inserted: ❬rId4:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:1276350❘lang=de-DE❭.❘lang=de-DE❭ ❬This paragraph has the image ❬rId5:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:1276350❘lang=de-DE❭ in the middle.❘lang=de-DE,spacing={after=140,before=0}❭"""; return arguments("imageReplacementInGlobalParagraphsTest", - OpcStamperConfigurations.standard(), + OfficeStamperConfigurations.standard(), context, template, expected); @@ -1008,7 +1008,7 @@ private static Arguments imageReplacementInGlobalParagraphsTestWithMaxWidth() { ❬In this paragraph, an image of Mona Lisa is inserted: ❬rId4:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:635000❘lang=de-DE❭.❘lang=de-DE❭ ❬This paragraph has the image ❬rId5:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:635000❘lang=de-DE❭ in the middle.❘lang=de-DE,spacing={after=140,before=0}❭"""; return arguments("imageReplacementInGlobalParagraphsTestWithMaxWidth", - OpcStamperConfigurations.standard(), + OfficeStamperConfigurations.standard(), context, template, expected); @@ -1029,7 +1029,7 @@ private static Arguments leaveEmptyOnExpressionErrorTest() { var expected = """ Leave me empty . ❬❘u=single❭"""; - var config = OpcStamperConfigurations.standard() + var config = OfficeStamperConfigurations.standard() .setFailOnUnresolvedExpression(false) .leaveEmptyOnExpressionError(true); return arguments("leaveEmptyOnExpressionErrorTest", @@ -1040,7 +1040,7 @@ private static Arguments leaveEmptyOnExpressionErrorTest() { } private static Arguments lineBreakReplacementTest() { - var config = OpcStamperConfigurations.standard() + var config = OfficeStamperConfigurations.standard() .setLineBreakPlaceholder("#"); var context = new Contexts.Name(null); var template = getResource(Path.of("LineBreakReplacementTest.docx")); @@ -1080,7 +1080,7 @@ private static Arguments mapAccessorAndReflectivePropertyAccessorTest_shouldReso Paragraph end """; - var config = OpcStamperConfigurations.standard() + var config = OfficeStamperConfigurations.standard() .setFailOnUnresolvedExpression(false) .setLineBreakPlaceholder("\n") .addResolver(Resolvers.nullToDefault("N/C")) @@ -1114,7 +1114,7 @@ private static Arguments nullPointerResolutionTest_testWithDefaultSpel() { Deal with: ${nullish.li[2] ?: "Nullish value!!"} """; - var config = OpcStamperConfigurations.standard() + var config = OfficeStamperConfigurations.standard() .setFailOnUnresolvedExpression(false); return arguments("nullPointerResolutionTest_testWithDefaultSpel", @@ -1127,7 +1127,7 @@ private static Arguments nullPointerResolutionTest_testWithDefaultSpel() { private static Arguments customCommentProcessor() { return arguments( "customCommentProcessor", - OpcStamperConfigurations.standard() + OfficeStamperConfigurations.standard() .addCommentProcessor( ICustomCommentProcessor.class, CustomCommentProcessor::new), @@ -1160,7 +1160,7 @@ private static Arguments nullPointerResolutionTest_testWithCustomSpel() { // Beware, this configuration only autogrows pojos and java beans, // so it will not work if your type has no default constructor and no setters. - var config = OpcStamperConfigurations.standard() + var config = OfficeStamperConfigurations.standard() .setSpelParserConfiguration(new SpelParserConfiguration(true, true)) .setEvaluationContextConfigurer(EvaluationContextConfigurers.noopConfigurer()) @@ -1227,7 +1227,7 @@ public static Stream tests() { @ParameterizedTest(name = "{0}") void features( String ignoredName, - OpcStamperConfiguration config, + OfficeStamperConfiguration config, Object context, InputStream template, String expected diff --git a/src/test/java/pro/verron/docxstamper/test/FailOnUnresolvedPlaceholderTest.java b/src/test/java/pro/verron/docxstamper/test/FailOnUnresolvedPlaceholderTest.java index cb28c148..a4584770 100644 --- a/src/test/java/pro/verron/docxstamper/test/FailOnUnresolvedPlaceholderTest.java +++ b/src/test/java/pro/verron/docxstamper/test/FailOnUnresolvedPlaceholderTest.java @@ -1,8 +1,8 @@ package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.api.OpcStamperException; -import pro.verron.docxstamper.preset.OpcStamperConfigurations; +import pro.verron.docxstamper.api.OfficeStamperException; +import pro.verron.docxstamper.preset.OfficeStamperConfigurations; import java.io.IOException; import java.io.InputStream; @@ -22,10 +22,10 @@ void fails() throws IOException { var context = new Name("Homer"); try (var template = getResource(Path.of("FailOnUnresolvedExpressionTest" + ".docx"))) { - var config = OpcStamperConfigurations.standard() + var config = OfficeStamperConfigurations.standard() .setFailOnUnresolvedExpression(true); var stamper = new TestDocxStamper<>(config); - assertThrows(OpcStamperException.class, + assertThrows(OfficeStamperException.class, () -> stamper.stampAndLoad(template, context)); } } @@ -35,7 +35,7 @@ void doesNotFail() throws IOException { Name context = new Name("Homer"); try (InputStream template = getResource(Path.of( "FailOnUnresolvedExpressionTest.docx"))) { - var config = OpcStamperConfigurations.standard() + var config = OfficeStamperConfigurations.standard() .setFailOnUnresolvedExpression(false); var stamper = new TestDocxStamper<>(config); assertDoesNotThrow(() -> stamper.stampAndLoad(template, context)); diff --git a/src/test/java/pro/verron/docxstamper/test/MultiSectionTest.java b/src/test/java/pro/verron/docxstamper/test/MultiSectionTest.java index 0a3c8e0f..7789f255 100644 --- a/src/test/java/pro/verron/docxstamper/test/MultiSectionTest.java +++ b/src/test/java/pro/verron/docxstamper/test/MultiSectionTest.java @@ -1,7 +1,7 @@ package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.preset.OpcStamperConfigurations; +import pro.verron.docxstamper.preset.OfficeStamperConfigurations; import java.nio.file.Path; @@ -17,7 +17,7 @@ class MultiSectionTest { void expressionsInMultipleSections() { var context = new NamesContext("Homer", "Marge"); var template = getResource(Path.of("MultiSectionTest.docx")); - var configuration = OpcStamperConfigurations.standard(); + var configuration = OfficeStamperConfigurations.standard(); var stamper = new TestDocxStamper(configuration); var actual = stamper.stampAndLoadAndExtract(template, context); String expected = """ diff --git a/src/test/java/pro/verron/docxstamper/test/MultiStampTest.java b/src/test/java/pro/verron/docxstamper/test/MultiStampTest.java index 5e5d0df5..a1ed4b86 100644 --- a/src/test/java/pro/verron/docxstamper/test/MultiStampTest.java +++ b/src/test/java/pro/verron/docxstamper/test/MultiStampTest.java @@ -1,7 +1,7 @@ package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.preset.OpcStamperConfigurations; +import pro.verron.docxstamper.preset.OfficeStamperConfigurations; import java.nio.file.Path; @@ -16,7 +16,7 @@ class MultiStampTest { @Test void expressionsAreResolvedOnMultiStamp() { - var config = OpcStamperConfigurations.standard(); + var config = OfficeStamperConfigurations.standard(); var context = names("Homer","Marge","Bart","Lisa","Maggie"); var stamper = new TestDocxStamper<>(config); diff --git a/src/test/java/pro/verron/docxstamper/test/NullPointerResolutionTest.java b/src/test/java/pro/verron/docxstamper/test/NullPointerResolutionTest.java index 5db63139..ef0c14be 100644 --- a/src/test/java/pro/verron/docxstamper/test/NullPointerResolutionTest.java +++ b/src/test/java/pro/verron/docxstamper/test/NullPointerResolutionTest.java @@ -1,8 +1,8 @@ package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.api.OpcStamperException; -import pro.verron.docxstamper.preset.OpcStamperConfigurations; +import pro.verron.docxstamper.api.OfficeStamperException; +import pro.verron.docxstamper.preset.OfficeStamperConfigurations; import java.io.IOException; import java.nio.file.Path; @@ -23,10 +23,10 @@ void nullPointerResolutionTest_testThrowingCase() throws IOException { var context = new NullishContext("Fullish1", subContext, null, null); try (var template = getResource(Path.of("NullPointerResolution.docx"))) { - var configuration = OpcStamperConfigurations.standard(); + var configuration = OfficeStamperConfigurations.standard(); var stamper = new TestDocxStamper(configuration); assertThrows( - OpcStamperException.class, + OfficeStamperException.class, () -> stamper.stampAndLoadAndExtract(template, context) ); } diff --git a/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java b/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java index b271fb9d..e7641e4b 100644 --- a/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java +++ b/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java @@ -1,7 +1,7 @@ package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.preset.OpcStamperConfigurations; +import pro.verron.docxstamper.preset.OfficeStamperConfigurations; import java.nio.file.Path; @@ -18,7 +18,7 @@ void expressionReplacementInHeaderAndFooterTest() { var context = new Name("Homer Simpson"); var template = getResource( Path.of("ExpressionReplacementInHeaderAndFooterTest.docx")); - var configuration = OpcStamperConfigurations.standard() + var configuration = OfficeStamperConfigurations.standard() .setFailOnUnresolvedExpression(false); var stamper = new TestDocxStamper(configuration); var actual = stamper.stampAndLoadAndExtract(template, context); diff --git a/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java b/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java index 13f5a476..5e040e86 100644 --- a/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java +++ b/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java @@ -2,7 +2,7 @@ import org.docx4j.dml.wordprocessingDrawing.Anchor; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.preset.OpcStamperConfigurations; +import pro.verron.docxstamper.preset.OfficeStamperConfigurations; import java.nio.file.Path; import java.util.List; @@ -20,7 +20,7 @@ void expressionReplacementInTextBoxesTest() { var context = new Name("Bart Simpson"); var template = getResource(Path.of("ExpressionReplacementInTextBoxesTest" + ".docx")); - var configuration = OpcStamperConfigurations.standard() + var configuration = OfficeStamperConfigurations.standard() .setFailOnUnresolvedExpression(false); var stamper = new TestDocxStamper(configuration); var actual = stamper.stampAndLoadAndExtract(template, context, Anchor.class); diff --git a/src/test/java/pro/verron/docxstamper/test/RepeatDocPartBadPlaceholderTest.java b/src/test/java/pro/verron/docxstamper/test/RepeatDocPartBadPlaceholderTest.java index a0474d2d..e1e4ee43 100644 --- a/src/test/java/pro/verron/docxstamper/test/RepeatDocPartBadPlaceholderTest.java +++ b/src/test/java/pro/verron/docxstamper/test/RepeatDocPartBadPlaceholderTest.java @@ -4,8 +4,8 @@ import org.junit.jupiter.api.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import pro.verron.docxstamper.api.OpcStamperException; -import pro.verron.docxstamper.preset.OpcStamperConfigurations; +import pro.verron.docxstamper.api.OfficeStamperException; +import pro.verron.docxstamper.preset.OfficeStamperConfigurations; import pro.verron.docxstamper.test.Contexts.Characters; import pro.verron.docxstamper.test.Contexts.Role; @@ -35,11 +35,11 @@ public void testBadExpressionShouldNotBlockCallerThread() { List.of(new Role("Homer Simpson", "Dan Castellaneta"), new Role("Marge Simpson", "Julie Kavner"), new Role("Bart Simpson", "Nancy Cartwright"))); - var configuration = OpcStamperConfigurations.standard(); + var configuration = OfficeStamperConfigurations.standard(); var stamper = new TestDocxStamper<>(configuration); var exception = assertThrows( - OpcStamperException.class, + OfficeStamperException.class, () -> stamper.stampAndLoadAndExtract(template, context)); String expectedErrorInfo = "someUnknownField"; diff --git a/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java b/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java index 888b1387..5292baaf 100644 --- a/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java +++ b/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java @@ -1,8 +1,8 @@ package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.api.OpcStamperException; -import pro.verron.docxstamper.preset.OpcStamperConfigurations; +import pro.verron.docxstamper.api.OfficeStamperException; +import pro.verron.docxstamper.preset.OfficeStamperConfigurations; import java.io.IOException; import java.nio.file.Path; @@ -20,9 +20,9 @@ class SpelInjectionTest { void spelInjectionTest() throws IOException { var context = Contexts.empty(); try (var template = getResource(Path.of("SpelInjectionTest.docx"))) { - var configuration = OpcStamperConfigurations.standard(); + var configuration = OfficeStamperConfigurations.standard(); var stamper = new TestDocxStamper<>(configuration); - assertThrows(OpcStamperException.class, + assertThrows(OfficeStamperException.class, () -> stamper.stampAndLoadAndExtract(template, context)); } diff --git a/src/test/java/pro/verron/docxstamper/test/StampTableTest.java b/src/test/java/pro/verron/docxstamper/test/StampTableTest.java index 38cbec10..f2c3ffd5 100644 --- a/src/test/java/pro/verron/docxstamper/test/StampTableTest.java +++ b/src/test/java/pro/verron/docxstamper/test/StampTableTest.java @@ -1,7 +1,7 @@ package pro.verron.docxstamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.preset.OpcStamperConfigurations; +import pro.verron.docxstamper.preset.OfficeStamperConfigurations; import java.nio.file.Path; import java.util.List; @@ -19,7 +19,7 @@ void stampTableTest() { var testDocx = getResource(Path.of("StampTableTest.docx")); - var configuration = OpcStamperConfigurations.standard(); + var configuration = OfficeStamperConfigurations.standard(); var stamper = new TestDocxStamper<>(configuration); String string = stamper.stampAndLoadAndExtract( diff --git a/src/test/java/pro/verron/docxstamper/test/Stringifier.java b/src/test/java/pro/verron/docxstamper/test/Stringifier.java index b57d2e4b..834592f6 100644 --- a/src/test/java/pro/verron/docxstamper/test/Stringifier.java +++ b/src/test/java/pro/verron/docxstamper/test/Stringifier.java @@ -16,7 +16,7 @@ import org.docx4j.openpackaging.parts.WordprocessingML.CommentsPart; import org.docx4j.wml.*; import org.docx4j.wml.Comments.Comment; -import pro.verron.docxstamper.api.OpcStamperException; +import pro.verron.docxstamper.api.OfficeStamperException; import java.math.BigInteger; import java.security.MessageDigest; @@ -56,7 +56,7 @@ private static MessageDigest findDigest() { try { return MessageDigest.getInstance("SHA-1"); } catch (NoSuchAlgorithmException e) { - throw new OpcStamperException(e); + throw new OfficeStamperException(e); } } diff --git a/src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java b/src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java index 2d045ab0..72ba9c37 100644 --- a/src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java +++ b/src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java @@ -9,8 +9,8 @@ import org.docx4j.openpackaging.parts.relationships.RelationshipsPart; import org.docx4j.relationships.Relationship; import org.docx4j.wml.P; -import pro.verron.docxstamper.api.LoadingOpcStamper; -import pro.verron.docxstamper.api.OpcStamperConfiguration; +import pro.verron.docxstamper.api.LoadingOfficeStamper; +import pro.verron.docxstamper.api.OfficeStamperConfiguration; import pro.verron.docxstamper.preset.OfficeStampers; import java.io.IOException; @@ -32,16 +32,16 @@ */ public final class TestDocxStamper { - private final LoadingOpcStamper stamper; + private final LoadingOfficeStamper stamper; private WordprocessingMLPackage document; /** *

Constructor for TestDocxStamper.

* - * @param config a {@link OpcStamperConfiguration} object + * @param config a {@link OfficeStamperConfiguration} object * @since 1.6.6 */ - public TestDocxStamper(OpcStamperConfiguration config) { + public TestDocxStamper(OfficeStamperConfiguration config) { Function loader = inputStream -> { try { return WordprocessingMLPackage.load(inputStream); @@ -49,8 +49,8 @@ public TestDocxStamper(OpcStamperConfiguration config) { throw new RuntimeException(e); } }; - stamper = new LoadingOpcStamper<>(loader, - OfficeStampers.docxStamper(config)); + stamper = new LoadingOfficeStamper<>(loader, + OfficeStampers.docxStamper(config)); } /** From c032acdd5ae4f4f9a19e7e851cc94afbc915d548 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 21 Mar 2024 01:40:16 +0100 Subject: [PATCH 049/134] Rename LoadingOfficeStamper to StreamStamper The commit renames and updates the functionality and annotations of the 'LoadingOfficeStamper' class to 'StreamStamper'. The change was implemented to improve clarity and maintain codebase consistency, impacting files and methods referencing this class. This renamed class now focuses on handling streams of data while still executing stamp operations. --- .../api/{LoadingOfficeStamper.java => StreamStamper.java} | 2 +- .../java/pro/verron/docxstamper/test/TestDocxStamper.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) rename src/main/java/pro/verron/docxstamper/api/{LoadingOfficeStamper.java => StreamStamper.java} (95%) diff --git a/src/main/java/pro/verron/docxstamper/api/LoadingOfficeStamper.java b/src/main/java/pro/verron/docxstamper/api/StreamStamper.java similarity index 95% rename from src/main/java/pro/verron/docxstamper/api/LoadingOfficeStamper.java rename to src/main/java/pro/verron/docxstamper/api/StreamStamper.java index 33460463..d6f37531 100644 --- a/src/main/java/pro/verron/docxstamper/api/LoadingOfficeStamper.java +++ b/src/main/java/pro/verron/docxstamper/api/StreamStamper.java @@ -15,7 +15,7 @@ * @version ${version} * @since 1.6.4 */ -public class LoadingOfficeStamper { +public class StreamStamper { private final Function loader; private final OfficeStamper stamper; diff --git a/src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java b/src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java index 72ba9c37..81951f07 100644 --- a/src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java +++ b/src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java @@ -9,8 +9,8 @@ import org.docx4j.openpackaging.parts.relationships.RelationshipsPart; import org.docx4j.relationships.Relationship; import org.docx4j.wml.P; -import pro.verron.docxstamper.api.LoadingOfficeStamper; import pro.verron.docxstamper.api.OfficeStamperConfiguration; +import pro.verron.docxstamper.api.StreamStamper; import pro.verron.docxstamper.preset.OfficeStampers; import java.io.IOException; @@ -32,7 +32,7 @@ */ public final class TestDocxStamper { - private final LoadingOfficeStamper stamper; + private final StreamStamper stamper; private WordprocessingMLPackage document; /** @@ -49,8 +49,8 @@ public TestDocxStamper(OfficeStamperConfiguration config) { throw new RuntimeException(e); } }; - stamper = new LoadingOfficeStamper<>(loader, - OfficeStampers.docxStamper(config)); + stamper = new StreamStamper<>(loader, + OfficeStampers.docxStamper(config)); } /** From fca7678300c1d6f9388ff19a225c8c493b9909bd Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 21 Mar 2024 01:41:29 +0100 Subject: [PATCH 050/134] Expand StreamStamper documentation The StreamStamper class has been refactored to better align with structured design principles. It now delegates stamping operations to an OfficeStamper instance. In tandem with this change, documentation has also been significantly enhanced to clarify the role and functionality of each component, with particular emphasis on the stamping process and exception handling. --- .../verron/docxstamper/api/StreamStamper.java | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/api/StreamStamper.java b/src/main/java/pro/verron/docxstamper/api/StreamStamper.java index d6f37531..4034d52b 100644 --- a/src/main/java/pro/verron/docxstamper/api/StreamStamper.java +++ b/src/main/java/pro/verron/docxstamper/api/StreamStamper.java @@ -7,20 +7,33 @@ import java.util.function.Function; /** - * This is an interface that defines the contract for stamping - * templates with context and writing the result to an {@link OutputStream}. + * This class implements the functionality of an OfficeStamper meant for dealing with streams of data. It delegates the executing + * of the stamp operation to an OfficeStamper instance while providing the necessary mechanisms to work with streams. * - * @param The type of the template that can be stamped. + * @param The type of the template that can be stamped. This type must extend OpcPackage. * @author Joseph Verron * @version ${version} * @since 1.6.4 */ public class StreamStamper { + /** + * Holds a reference to a function that takes in an InputStream and produces an instance of type T. + */ private final Function loader; + + /** + * Holds a reference to an OfficeStamper used to execute the stamp operation. + */ private final OfficeStamper stamper; - public LoadingOfficeStamper( + /** + * Constructs a new StreamStamper with the provided loader and stamper. + * + * @param loader A Function that takes in an InputStream and produces an instance of type T. + * @param stamper An OfficeStamper used to execute the stamp operation. + */ + public StreamStamper( Function loader, OfficeStamper stamper ) { @@ -29,12 +42,18 @@ public LoadingOfficeStamper( } /** - * Stamps the template with the context and writes the result to the outputStream. + * Stamps the template present in the given InputStream with the context given + * and writes the result to the provided OutputStream. + * This method first uses the loader to load the template from the + * InputStream into a type T instance, + * then uses the stamper + * to perform the stamp operation using the template and context, + * writing the result out to the OutputStream. * * @param inputStream template to stamp * @param context context to use for stamping * @param outputStream output stream to write the result to - * @throws OfficeStamperException if the stamping fails + * @throws OfficeStamperException if the stamping fails for any reason */ public void stamp( InputStream inputStream, From 1da87a714672556303cc9eeaed08a71d830307c6 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 21 Mar 2024 11:43:42 +0100 Subject: [PATCH 051/134] Add detailed description to ITypeResolver interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A comprehensive description has been added to the ITypeResolver interface to clarify its role of mapping a specific Java object to an object of the DOCX4J API. Furthermore, it has been marked as deprecated with details regarding its newer, more efficient, and versatile replacement — ObjectResolver. This is part of ongoing efforts to simplify and improve the internal workings of the docx-stamper project. --- .../verron/docxstamper/api/ITypeResolver.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/main/java/pro/verron/docxstamper/api/ITypeResolver.java b/src/main/java/pro/verron/docxstamper/api/ITypeResolver.java index 45e87836..1870b44a 100644 --- a/src/main/java/pro/verron/docxstamper/api/ITypeResolver.java +++ b/src/main/java/pro/verron/docxstamper/api/ITypeResolver.java @@ -3,6 +3,31 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; +/** + *

+ * A type resolver is responsible for mapping an object of a certain Java class to an object of the DOCX4J api that + * can be put into the .docx document. Type resolvers are used to replace + * expressions within the .docx template. + *

+ *

+ * Example: if an expression returns a Date object as result, this date object is passed to a DateResolver which + * creates a org.docx4j.wml.R object (run of text) containing the properly formatted date string. + *

+ *

+ * To use your own type resolver, implement this interface and register your implementation by calling + * DocxStamper.getTypeResolverRegistry().addTypeResolver(). + *

+ * + * @param the type of the object this type resolver is responsible for. + * @author Joseph Verron + * @author Tom Hombergs + * @version ${version} + * @since 1.0.0 + * @deprecated as of version 1.6.7, replaced by {@link ObjectResolver}. + * The new resolver is more versatile, requires less reflection mechanism, + * and simplifies the internal workings of the docx-stamper project. + */ +@Deprecated(since = "1.6.7", forRemoval = true) public interface ITypeResolver { /** * This method is called when an expression is found in the .docx template. From 6ea9358c8d81f6548c40c8bb5ac33191bdb3fcff Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 24 Mar 2024 13:17:48 +0100 Subject: [PATCH 052/134] Open and export docxstamper.core module for testing The changes open the pro.verron.docxstamper.core module and also export it to the pro.verron.officestamper.test module. These modifications are done to facilitate testing by providing access to the core functionality of docxstamper. --- src/main/java/module-info.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 011459c1..d9c3fe97 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -11,6 +11,9 @@ opens pro.verron.docxstamper.api; opens pro.verron.docxstamper.preset; + opens pro.verron.docxstamper.core to pro.verron.officestamper.test; + exports pro.verron.docxstamper to pro.verron.officestamper.test; + exports pro.verron.docxstamper.api; exports pro.verron.docxstamper.preset; From 1842ee88dfebfc8b5a01697a9647ea5ff8bcc412 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 24 Mar 2024 13:19:46 +0100 Subject: [PATCH 053/134] Add BasicWordTest for stamper functionality A new test case is created, BasicWordTest.java, within the 'test' directory. This test verifies the functionality of the 'stamper' using a Word document as a template and checks its result against an expected output. --- .../docxstamper/test/BasicWordTest.java | 27 ++++++++++++++++++ test/sources/word-base.docx | Bin 0 -> 12250 bytes 2 files changed, 27 insertions(+) create mode 100644 src/test/java/pro/verron/docxstamper/test/BasicWordTest.java create mode 100644 test/sources/word-base.docx diff --git a/src/test/java/pro/verron/docxstamper/test/BasicWordTest.java b/src/test/java/pro/verron/docxstamper/test/BasicWordTest.java new file mode 100644 index 00000000..d42e1685 --- /dev/null +++ b/src/test/java/pro/verron/docxstamper/test/BasicWordTest.java @@ -0,0 +1,27 @@ +package pro.verron.docxstamper.test; + +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static pro.verron.docxstamper.preset.OfficeStamperConfigurations.standardWithPreprocessing; + +public class BasicWordTest { + @Test + public void testStamper() + throws IOException { + var stamperConfiguration = standardWithPreprocessing(); + var stamper = new TestDocxStamper<>(stamperConfiguration); + var templatePath = Path.of("test", "sources", "word-base.docx"); + var templateStream = Files.newInputStream(templatePath); + record Person(String name) {} + var context = new Person("Bart"); + var actual = stamper.stampAndLoadAndExtract(templateStream + , context); + var expected = "Hello, Bart!"; + assertEquals(expected, actual); + } +} diff --git a/test/sources/word-base.docx b/test/sources/word-base.docx new file mode 100644 index 0000000000000000000000000000000000000000..fb650493a840fa814d4b4f2fa7ac90dab8e0555f GIT binary patch literal 12250 zcmeHt^40gC>_UY~auu-6;;R-=+j4>REEpEzWN; zyf?w6Tu)+N5gg?7w_$JZz?IM-}l*@$xiY~Rfux} z200U)#Ngowq$#M!z#O^|xv(qJ4fK;1xf_;7R{N;vc(&FQ(8~c|uU)7XQZ(ExF^DjM z#gBc@$g9-IUCw%@hAD~$Yv&UM0SQoO;@9v1ygPEiSTX|DVyDCH7zKIX!_)9u_{6N2 z;o>bIay_rcw-=T&BMQbGFTgDGK?40N)i#x5mTLf(z3KP5A0_6@$6+pQbKVgQYy<^)Ms8 zI}Umbn(RcYvFV=|rq;w-|zYD>ys02i$0pC0)Z7Ql7)48@11rV}T;oHNB-*OGKk z@KRKfB0AvaZuNS!@7p~9$sz59EFNc?{PN-b|o2m5m`T zwtJ;cw^wC9v4u6-isq6Y&xVf+weNN*UKpRq6vrZ76DXY{UTOP_P93|1DKz3b5NYb{S7`a9_6>^EAq4IUA{|H zBt9t<&xj#=G2W!$&tGGx9O?#=I{kvhYaXR3q;5D!qFuRWZds~gjgAJ4yRu?lp($oel50H8_zR? z#DbW-a>JKnJ<*!YJX1lWvV@3)nfp3F<^rIdQuM*>s{}FCRi|S-y*O=CfrbZ(Xf8d@Bt1~v;Vu1u zLpe9tv`}7}{G{oc?TawzzHqN&LOd zp=?k070d#X_O-lYXP~25mA5A_g0K+H=ny(}KKdlIa)5I76RxpRh8WYAI(Dp`0T)?q z3DbEYz+ON#JXXienU^UEry!E59Ca^ostil3=~egL120+TBQE0eg%L$VYdoFUB?zIg z`AP@U`SAjQGI!h1$Ly)M4YdmU3WBLdv2_ifMT8-T9R!z!RbfJK;MRhvg|HH%+_XQ^ zA0>i#IF|VpQC(rJ}%P%*0-xqtC zRa)U5R{!B8wfj#inlq_aym+x5mKRHU4S)sv&5HgNYW`|Ve}o;dmkjX58vnbmiiF`8 zxBHR-g**kN`7-eODhO@bNRa^N9s#0+0wwftqZjL))e@2i69%78oT}2C$Nf2h<6RN% zQ8V7fIRj3pyHVBN^RynNgw;oasMhM%2L!Y@@dny@dNx@TR&vramjNPq+)5KH9~zTP z-n?cv<>#t;P4zZvd1l@L7;5{mB=hD61O9&CLJ+B6lu&_tUzD(HNUU)t6^4or*2)=G~-hf zQU7sGnsbZhRPJdET}LxUZ1yfSi9Em8?LTTw^P_jkcjy=2OAG*Dy(q(PTH|72YHP~$ z+sN|M{~l^eN8*d4et|h73T++s8r`kcur7$-oUgKNuM3LpP_izn%(9$ZUTS0xHOO{? zgUpN*Mr@@(6+&d`-AoYn8w!-&cnDLumdLOy#p;|h=&mxDjjb;0tSYNw9L?->U@96z z+YYQL*h(2s)`6iC<|LI|aI#0b3kf}d4NpMJqwivHj|uTMyq=aH%O}8H$KfT;4kMUx zBBLqh?aRaIW=7vxz@%d4GUF3N|I9}p!~F)jgM~p&s!)qXjNI`PWM7UO>fXJ9+6NOSq0Ld1N6qjDuW=+1Nrf+yFEVX+Ks%>k{L>K|e$=kg*m~e0y49x}4 z-#3nKH>ARb80vUhzdUIVFhGhWMx=n&*QF-TeX8g5_T~Y!T#g-H)pO;Q7nT4#WW!<_ zY-RHfbPog@jW%n}cjfpuP1XF+dO=e~%>l>PK_j6&3QHGUCw|3?dl`2e z)V6b+CzncX2eU}+(4tA?iAXMk)v7^TcIt5~`Kc8rMaP#1$xwX4Sk9BlLsU-M6@9ym zAYXvR4yh!%_*k(=RH#1!D#eFPZg27^;*%n3L-0`PWTj4(kmr5SUdk2qQWrJVCuF3) z{BH?{k2w{}dtT3r-OY8T$VRR$#$57|;f}lY^7mOjM-Pt& z*VTmYZNEkGJ#CHaw?90_J}WUHX|3}4-X88n+IK!26n}f=@iD)L&*z;HeWvxzLhGQ> zLXIhz&%mmj5h_VSss&ZL7lA@8l=o(bc^aCHei4J(n z{-Z13H?gwhsU}9kMm6Z7KV+$Ng^`wWwa!j&x1?&Z@?(Dmwi7P z^ek5k0Go-C79m7NM_PVJ7?@>!;~^lkQC``ujW+^jYUJm$Aw*BC^rYn(vv)7Fx-N#A zEB9>U?$M!{Bug)teWV+RQk|8r`5MD$M;}Ac4Z8^Ox{kr;YR5X)Hb79b|h}E zOBGuil#EOzMoXbXpv4=0K+1X5ta=a)i)&bT*G*XMY??39Q|35K^{6dd1&(d_a!QF0Z*4itqP}Cr|IlsC zgKU~1BW`_p2(}{CaTIbVc#za53GJUd0h&=h0#@}F}JfS(G22b!&b~X6$U$qW*382nFNoL!JQgA z)Gu{}bxX)CK1?YsOX`{kBfM2u60tVR9YU7XZc9q{$0-@bJiV_^%Cx>G73dTPWh*7V z0>QZy{ov3$Q1!#8|Aip?8#m;=s8taI>P|g1=+|f@>T|ErvJj3&BCPkU95JJ>85Yq* zUGQ@Iq7&q|X>^*^U7YSGWaiyXudqMzASNfS+|T)T#AOUnj51({edhRa3+mB5<5AOG z;nFB9^?p0gk=kkpWH^1o7K+hoN)wc2X;K&#leH?=K;2By4%u$v-Dt26x-3z}?yaku z>4?S#9-c6Y_+gjL;wB3zhv+lBh8k8+u%w*GA3k)Ff5RrHb@Z69dKDN!geis8seV$X zS2}S5oD~9Imv3e1GrIC7!x&)V_L&OkwxI}I?fIN;=*8N%x5uUyUbT`$M#{{0i855^ zucXrT58pS)xj|d4xWZ~|`&CZat9|^Wm8)rcjiA>&P0b#pRXkA%b!pd$e-mx?9B}a3 zvSz*tTCZ}RdS*ya;lzqUv~!YvrbDXm$1E^9Yu>M>hU3uZwu8NTyYZgsCD{FE617ey zS>yl-0MsD<<*$gdsf&xHow@VRP}rajv;%RX`Z7(tXTH!8PaWWL{UmB5F8k`#4y~Aa zf=eoop0t2EE{RF=VwJZ*WZ3SzXaXq{3y1mkhyy$J*7OcReh1R4gcGOz8|U93KtiyD;K{qol&~Lm4r&=Q(q$A@RU>f z{-l9$iq{!E7+B2FQ^-oF8$i?hRa!GnN{jPKj}w3(U3WW4!5O91QF+|4@mZU(E~wAC zdm1`h0joswHj54H%*D)w4iM_w+t^D0|0XaL5Z3|0>nQMuh4|69wJL)Tf_ z2NQb`E}@4+s>b`9F9tENCPj4{vh)F?<>j{VLQn}GXs4oXYz2{!UA*VEB&Oz7WFeLl zMTjRaOte|)qmSk&u&HxVk(1<5R{X|Ail2qhFJpN&KE)sKajP?vW$~DI*s2)D3*~?H zKjr&23rM0GSBIy`2{Q?y;lQ9O#0mIj+wdx^tD*+oIC1^;tjgOwlR^(DDSSw5d`}QG zDQ~@ z^WRp_qSaYZ5;G@-30lb&EhU(1tQNf5ygXLGPqXmBzt9waP1!_@>o-$vQh0^=%^@Aj zvwz<7sxT!2kU!Y_9JVG7_AaaIoQpc|n{SA5lnRpNJ+b-rXm65=G$|yuyD4%-#I)5W;z5%s{TqSfR1hD>jrv6>?` zOB9sM@}M$zTyMt(w)TTv-CeNm%#n;8m zcFIy$hJkQ(k3@`{#~wOw0hajsm8pmtLgq&{UDDbk4{anzHTcBi;Q(oQC3-)M_9{FV zI)z#G5Y^M3nnv=%&r~Brrk^S*RN<7LV9SONfnsTVu6v7O@DU@3soo|Lv%A}QL;|~K z-4bQik`ptYjCbeNw!X@>Q;p%NC1n}t@^eo!xhA^Q`X6aLhSr29_{a}(`oM#rkjw}b z%9s$Ae&i-Xsks%nTbibRqHn0HNkpo!nHUUJ}4@7=X zVwFIQpt3GnUz4-pL1w{~c4$l59^@LvmbZ5QnG{!;w}T)km(#wnbdihdGv4&jEKSgp zlf_P?w~0@VsE6Sk03W2PV?Z=aTg!@F|tcwp%4(xa^ zpo#PLPT-fuXDgk@f2<@TW^2plzf?&WodE!pKeBaa7f&10-<(tXCs}78H+tx?cH?tE z;q+lwyucPY$5;}m*LuDeSANrG3>8jOBQf|Dxv{t3BRDaIpk1I*(AzKMuL}KOH~N6@ z&V}zTn~UkA{hU-`)3c+IR}N!Iv%ayS-99ev-Y;*h02Nh|=;bwNo&7kyw)=LQQJx>Z zRmUDjp7Csn7Jzx4@<)z~Mb_zjA&QOk=wefAkC)Cm;M!3#t(ak&edIRY&(%8WW7V!x z8WEu#+Bsc+dNP?YP$7!Q92XrC7;G8x&|A8x-6*9TYS#!et<8ITmHSpInLatnQ*k$F zY$_v_CBB*k<{Ka>Tw_pZY?$lLAmf#060*QT^Lx`hXoof+ z++!xAXyH9@)=?1D39Onq)E~Br~8?1w=+t`E$2IUR8WHyGLVv@Dt| zI5ci`vm04!51rl5HnlFbJ#2Gs+g!{W9dDE`vQK}nM^pBmcE;egi>Z8C<=NO8R(Ja* z{R*Lth?A++?XJsY^_{XI`tbT-+iawL^4@#3rQ?q zvg3E}*@FLWx$m?EZS*t^Y4p?sZuC?GYV?#FZ1fb-h5Z{_%;f8FW>E(z+abXr<&ny zrIc2BPnEOcb@mkNlu9K_53?yy#3nHuv#XgkJ}jgMZ25mUms1>+H2 zUt|~3&aHgb5Jjv;f(0Ro!fBmS79kw&bi{b;hUW+k3@5>iN}!^iDO+G|tI4$qeHK;k zax-ZHdB~;z+~s+9ng}Z5epH_nvVDe$$?bY1b?9|+fCy1J`<6&WHM{UgUFbaYE4Fs_ znzZ%gfIdVf#Mh%GpG$JlE>(od_G?1LX6!F-3ig|Sen5&YS;bpA@oU2{wNh?x+vnbW ze{@8ey#2X*%}u7yckBY(;KpS-lP%gV-RL@k0Xv?DWjV$*7Pl=smen)nhJvnTjoDDl_@}5hTqsf+Lm^qN-Y{;ClH2spdMB-Ga1>ISz{8oIn-xI;+q$Bc#?5x z*byoIKz}HND#OYlNhw0!F@o7joG#38?Eb`;C_};Cpxt-y(O*hVB(u&&lj#j?d*S&; z{+Iw{Imz%nigI@*b{^|20o(|Wt%ged^O=W7**z^;0-NxAmBP2r7O9^(GmyD_(rZHS zzlhXW?eBF(=?4ZacOwZbFub1a(krN-4!%aWVvgN+Q0LnR5$HW|@TE&}R4aW()&U#z za)2T~5K33m)Keg=-9jU**-|88T92#c3siPWxwfZ3XJw&}l7d6XPw46{VXt9U0t;Qu z1$_-&E&4f>EBAz@0Fa}sGUMf*MbtfCOCyT?zcf8PKxcayy z%2;-Y_b4;kHy4#?i?YJwvX%YDVddiH8nh{Pw)are=eZm)h(~$$lN@SwK_wzjUFIgT zZ1h8JXqjlHmb4U;t&O9>#$?UMx`k2S!N8r8DIt4`m+Ka-BQc36U`)+H?5imqPVBH6Ge5bupp--tUJd*S znHLkx18jpL3ApVH7#)-VxTy*%p^1uwAU+o9gjmSS%sM!txe87!8*LN{3vEF#4wllt zzJEceguVN18IlgryC*0Lp2i|y2y&?eHGs2BmkF7fHtHKLR{w_=gwS8-Xhm|O;n99B z9V#K_8z_kj*O{gC|U7Ju}0gNcQla_4v7 zpymrng(;Fi{d;f!g8aP2#R`VO{rB$ktbI6BsePLliPpxwyp9)+-|v-K&Tw5T+F`rS z5H(sQXU{%s2FEJ#i&UZz5-xh>6N!u{k^G>s8aH)KwYCp`r+f1N%DaanO65P1PR%eU zIpk0V2pr+Vjg`K3NAl!C$MpQBOST*hSw04;Iy?{yv^;5{+tbpovXh#eMuR<)Vv{=w zplYYn=3TFeY;$VBfc0*nW8)2|32Hm5#qdB+-+W-bD=-(3ox4s27n*l)fi3nEny(4g zs+?kB7t^172>!Bv3+HQkfuZ2##o}T3NZruwr;;`^VWv^q6TMGmP#79aTna|1V-*IU zf|u7_5E_m~$3bpTh%7(mClogWz+mJMfs#g2G*s#-Mf`0h6tZFlg35&HFPKQ)ikUe7 zWIXg5CE>q=f@pCWev11EibEym_!|5ot4m2(C3WVCekf&zI^>hXchV8KUfBUTVn!a` z0I=Bv3<3=Xp5U)ma8`{|uhkh^^Il$H_8jAGXqPJI;=<^p`%cj!r6VjJY}h%Usc*x=>wIy0ExsOGy6$+>I}Dqzs-Qk z3OWBJ=Jz-H2lVf264d|y0$QvF>0X}un{-#HmxtTAgc0Asmss|z;7|&}6hvPCEaLR-O$}FS8Kgk^2#iOW%6SvaeDm1VrB4tju z>bYeM1Ife2Pqf0$`mEO~$?rL^cn_&ufFboMw#Y|s?wIfpXwT|#`c(0x#BbkTw+KYQ zvF9rCTYM|FU%h8A|b;9#Ch>_%cYiw~71046F6$eQ<-hW;Lr*p9H9%#jk0VleJU^*$xc=gVx zwWTASOB3QiJuF^csXL;0WKTzhWiCCbfX*QzR3m6-6g@~ezSxlL6Fd>nD`#?+HlwkY z@@R`&ws_-fur(&VbpATn0SxPyaiHd9&$-Q*n#8KH|L=n47Sg0 z{+W|taKqf2M##9Xm>*)N>^2XXnD=0!nEXpabp?#wn90Snc#C#-WaMLL#xkENoS_xa zBFj=)`B%zqj8#>V^Y(Ru{Ul}OzOOX8Z@OGHoZ`$Z)%8|qo%B!;d~ecu4|Ek?a6Pbv zGwLD&Kd{KW4a5q)_eTSOATc-DYGr`ajeKb=21!IG!a3oB#dg*%1*$N8JR z5T;ZNjciPRYkefi+5wqSL$0B`U;^$nl1%8T(0C*=VW|~N6bs%pvx{nrh$<0V4)Y74 zOe45VgEsMSmFmjp81Y;uMHTTU#_`9B(~HrntK9S8n`H2-OhWU}#K00&uym@}F4kE( z)AGJ*kRmA246-(uL+g&~cYd}qqob&VBi7GI&%vdwtTZ>Yuo#47AyZ|8>q?#q)M%MK zP(XdORMuCZurj($Q2&0^g9E~J#d$=J(5mD%b&s%vOq|>KkfHicWh$wi6GrPCT`S*3 z(ONW}Wum@BD~ni%kY(})lAL>Xs3e&{kWE*V5NZ$YNZkf5i!8kZ> zap%Mchw819$0X=MljW^Hjg2C5UN4NkaH=#tO*qZH2xmpfAX+o_7@45C{k)Q8U4=iS z{ab%*Y>ygVq35Gbrp2*?Dk%12{*at>Nm62GLE0}f-(jpslMT^&n+k?dFXBLOZXLZI zQ5fzbx!Mxac}UK2ZX|YFSZM7_L@Lt`#!K%;85mIRQY?bs)wOwPEBkTa29^<1zLsiIaT+ zmkQ_8spIncovx#qEtu<=3viij&zh-4dpxNF{bhob%PssHz%0o@mw0`~vya)b5mT#a zkZzy=%C}BkGACa%);iEXsTYU#8c2ZWvt5A_5%KVUv}A zzJJT7`|u3w$}2Zs?R*~tw0)ZNEpB5zH~+_iqre9!VYe3#aQdQ!XfIvcmsYKUlf8p8 zld-+iZwmOr5c*%M_T}iJ67=PRm@)fSWgn0Wjwisal9*FU7K0qa!_Vb?%6}}Y!Pri_ z8Q)*MT1eX^^l59}aYsp=-UivKLgO>5QS%5niYNdQXc#w*g7wT2Pw=4T6bCI!$)hHa z>G804%z379m7%kurVKIx_#RZ2Y(?T0dzoX*O*U{?jqy|k*gHciGq-c}HAOa}*!~zo zRSr-wPW#k$h{DUfc^wqy9ff)y>p0dBh2gLy%pG6%-3+{8PJYWXK`SsI)$%DcaaZtg zuM+F6#=NUaF~k0K?CJf*Y3t*x!T=>5bjbkEaHazVCJ%qgm%4*-CW9*8BYb@}h#4AX zK57In-1a~gsiql0OIH1^Dz#8;0fAk<{w`LshI6jPRup7DU8RGfC#xOsn;%ElAD+NR zqXMEWR>T|^(S1K22OXtA5Z?ILF^&bDrK~KEQ{%lt=Rye$pikK%)2DL7F(G0^G&m@F@WeLj}Zp{~eE^L)Uvpev) zXjmc2&qUd}VEBS9=PMR8=maSOmbf}DszZ8mcJ+1y3m%ZvUC3J`yDpnhH7vp6w%+)@ zf=z)XK_{b&Kz{?OkS-3>on&UTfJf9Cw@U1k%>{+{74Pr&Q?{SkGS&SO^P}wFaPTsj z>}+{uy3P0#)Rqi9tr-xn1^9_Gf8zVGgg1LVvMT1@Isc@4wrHd^;r?cUOT6|2Oi$;_ zVgIX%0|w6Ul0p453HrC+ht z2ea*0_}@u9e}VykTe#oh{|`#fuabTxcKj)e>7}>zk2H^8Mf}BIt|a>%{7atvtAt;3yFVo`Q2Z|8Z<*e&_`gTyKhXd{ sH5CBxA94Cu_}_iyU*S5me}Vt*ITfU#Uex0!Js1Jd_2OM27=GUUA4J6GI{*Lx literal 0 HcmV?d00001 From b4296a9a67495c060ced72220f8a9e87ad52bc74 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 24 Mar 2024 13:20:17 +0100 Subject: [PATCH 054/134] Add intellij-style XML configuration file The new configuration file intellij-style.xml has been added to the project. This file contains specific settings for Java code style, including settings for wrapping, annotation parameters, binary operations, and others. The addition of this file will ensure consistent code formatting according to the project's guidelines. --- intellij-style.xml | 80 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 intellij-style.xml diff --git a/intellij-style.xml b/intellij-style.xml new file mode 100644 index 00000000..b37f0d59 --- /dev/null +++ b/intellij-style.xml @@ -0,0 +1,80 @@ + + + + + + From b748e83f44c1d1c1be35b2c833b4e9be4bc73d1f Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 24 Mar 2024 13:22:23 +0100 Subject: [PATCH 055/134] Implement support for PowerPoint document stamping This update introduces support for stamping PowerPoint documents in the library by creating and updating various classes necessary for this process. New file "PowerpointCollector.java" which extends "PowerpointVisitor.java" has been created to collect PowerPoint items is introduced to handle text replacement in the slides. Updates are also made to existing files like "OfficeStamperConfigurations.java" to support PowerPoint stamping configuration, and "OfficeStampers.java" for docxStamper and pptxStamper methods. Testing files are also added to validate the functionality. --- .../pro/verron/docxstamper/api/Paragraph.java | 6 +- .../docxstamper/core/PowerpointCollector.java | 34 ++ .../docxstamper/core/PowerpointParagraph.java | 315 ++++++++++++++++++ .../docxstamper/core/PowerpointRun.java | 35 ++ .../docxstamper/core/PowerpointStamper.java | 54 +++ .../docxstamper/core/PowerpointVisitor.java | 95 ++++++ .../docxstamper/core/StandardParagraph.java | 2 +- .../preset/OfficeStamperConfigurations.java | 203 ++++++++++- .../docxstamper/preset/OfficeStampers.java | 7 +- .../docxstamper/test/BasicPowerpointTest.java | 35 ++ .../verron/docxstamper/test/Stringifiers.java | 21 ++ test/sources/powerpoint-base.pptx | Bin 0 -> 33325 bytes 12 files changed, 799 insertions(+), 8 deletions(-) create mode 100644 src/main/java/pro/verron/docxstamper/core/PowerpointCollector.java create mode 100644 src/main/java/pro/verron/docxstamper/core/PowerpointParagraph.java create mode 100644 src/main/java/pro/verron/docxstamper/core/PowerpointRun.java create mode 100644 src/main/java/pro/verron/docxstamper/core/PowerpointStamper.java create mode 100644 src/main/java/pro/verron/docxstamper/core/PowerpointVisitor.java create mode 100644 src/test/java/pro/verron/docxstamper/test/BasicPowerpointTest.java create mode 100644 src/test/java/pro/verron/docxstamper/test/Stringifiers.java create mode 100644 test/sources/powerpoint-base.pptx diff --git a/src/main/java/pro/verron/docxstamper/api/Paragraph.java b/src/main/java/pro/verron/docxstamper/api/Paragraph.java index ebe7a06e..f3d8e858 100644 --- a/src/main/java/pro/verron/docxstamper/api/Paragraph.java +++ b/src/main/java/pro/verron/docxstamper/api/Paragraph.java @@ -1,9 +1,7 @@ package pro.verron.docxstamper.api; -import org.docx4j.wml.R; - -public interface Paragraph { - void replace(Placeholder placeholder, R replacement); +public interface Paragraph { + void replace(Placeholder placeholder, T replacement); String asString(); } diff --git a/src/main/java/pro/verron/docxstamper/core/PowerpointCollector.java b/src/main/java/pro/verron/docxstamper/core/PowerpointCollector.java new file mode 100644 index 00000000..e01f8e29 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/core/PowerpointCollector.java @@ -0,0 +1,34 @@ +package pro.verron.docxstamper.core; + + +import java.util.ArrayList; +import java.util.List; + +public class PowerpointCollector + extends PowerpointVisitor { + private final Class aClass; + private final List list = new ArrayList<>(); + + public PowerpointCollector(Class aClass) { + this.aClass = aClass; + } + + public static List collect( + Object template, + Class aClass + ) { + var collector = new PowerpointCollector<>(aClass); + collector.visit(template); + return collector.collect(); + } + + public List collect() { + return list; + } + + @Override + protected void before(Object object) { + if (aClass.isInstance(object)) + list.add(aClass.cast(object)); + } +} diff --git a/src/main/java/pro/verron/docxstamper/core/PowerpointParagraph.java b/src/main/java/pro/verron/docxstamper/core/PowerpointParagraph.java new file mode 100644 index 00000000..b75a1b32 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/core/PowerpointParagraph.java @@ -0,0 +1,315 @@ +package pro.verron.docxstamper.core; + +import org.docx4j.dml.*; +import org.docx4j.wml.R; +import pro.verron.docxstamper.api.Paragraph; +import pro.verron.docxstamper.api.Placeholder; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import static java.util.stream.Collectors.joining; + +/** + *

A "Run" defines a region of text within a docx document with a common set of properties. Word processors are + * relatively free in splitting a paragraph of text into multiple runs, so there is no strict rule to say over how many + * runs a word or a string of words is spread.

+ *

This class aggregates multiple runs so they can be treated as a single text, no matter how many runs the text + * spans. + * Call {@link #addRun(R, int)} to add all runs that should be aggregated. Then, call + * methods to modify the aggregated text. Finally, call {@link #asString()} to get the modified text. + * + * @author Joseph Verron + * @author Tom Hombergs + * @version ${version} + * @since 1.0.8 + */ +public class PowerpointParagraph + implements Paragraph { + private final List runs = new ArrayList<>(); + private final CTTextParagraph paragraph; + private int currentPosition = 0; + + /** + * Constructs a new ParagraphWrapper for the given paragraph. + * + * @param paragraph the paragraph to wrap. + */ + public PowerpointParagraph(CTTextParagraph paragraph) { + this.paragraph = paragraph; + recalculateRuns(); + } + + /** + * Recalculates the runs of the paragraph. This method is called automatically by the constructor, but can also be + * called manually to recalculate the runs after a modification to the paragraph was done. + */ + private void recalculateRuns() { + currentPosition = 0; + this.runs.clear(); + int index = 0; + for (Object contentElement : paragraph.getEGTextRun()) { + if (contentElement instanceof CTRegularTextRun r && !r.getT() + .isEmpty()) { + this.addRun(r, index); + } + index++; + } + } + + /** + * Adds a run to the aggregation. + * + * @param run the run to add. + */ + private void addRun(CTRegularTextRun run, int index) { + int startIndex = currentPosition; + int endIndex = currentPosition + run.getT() + .length() - 1; + runs.add(new PowerpointRun(startIndex, endIndex, index, run)); + currentPosition = endIndex + 1; + } + + public String asString(CTTextParagraph paragraph) { + StringBuilder builder = new StringBuilder(); + List egTextRun = paragraph.getEGTextRun(); + for (Object object : egTextRun) { + if (object instanceof CTRegularTextRun run) + builder.append(run.getT()); + else if (object instanceof CTTextField text) + builder.append(text.getT()); + else if (object instanceof CTTextLineBreak) + builder.append("\n"); + } + return builder.toString(); + } + + /** + * Replaces the given expression with the replacement object within + * the paragraph. + * The replacement object must be a valid DOCX4J Object. + * + * @param placeholder the expression to be replaced. + * @param replacement the object to replace the expression. + */ + @Override + public void replace(Placeholder placeholder, CTRegularTextRun replacement) { + String text = asString(); + String full = placeholder.expression(); + int matchStartIndex = text.indexOf(full); + if (matchStartIndex == -1) { + // nothing to replace + return; + } + int matchEndIndex = matchStartIndex + full.length() - 1; + List affectedRuns = getAffectedRuns(matchStartIndex, + matchEndIndex); + + boolean singleRun = affectedRuns.size() == 1; + + List runs = this.paragraph.getEGTextRun(); + if (singleRun) { + PowerpointRun run = affectedRuns.get(0); + + + boolean expressionSpansCompleteRun = + full.length() == run.run() + .getT() + .length(); + boolean expressionAtStartOfRun = matchStartIndex == run.startIndex(); + boolean expressionAtEndOfRun = matchEndIndex == run.endIndex(); + boolean expressionWithinRun = matchStartIndex > run.startIndex() && matchEndIndex < run.endIndex(); + + replacement.setRPr(run.run() + .getRPr()); + + if (expressionSpansCompleteRun) { + runs.remove(run.run()); + runs.add(run.indexInParent(), replacement); + recalculateRuns(); + } + else if (expressionAtStartOfRun) { + run.replace(matchStartIndex, matchEndIndex, ""); + runs.add(run.indexInParent(), replacement); + recalculateRuns(); + } + else if (expressionAtEndOfRun) { + run.replace(matchStartIndex, matchEndIndex, ""); + runs.add(run.indexInParent() + 1, replacement); + recalculateRuns(); + } + else if (expressionWithinRun) { + String runText = run.run() + .getT(); + int startIndex = runText.indexOf(full); + int endIndex = startIndex + full.length(); + String substring1 = runText.substring(0, startIndex); + CTRegularTextRun run1 = create(substring1, this.paragraph); + String substring2 = runText.substring(endIndex); + CTRegularTextRun run2 = create(substring2, this.paragraph); + runs.add(run.indexInParent(), run2); + runs.add(run.indexInParent(), replacement); + runs.add(run.indexInParent(), run1); + runs.remove(run.run()); + recalculateRuns(); + } + } + else { + PowerpointRun firstRun = affectedRuns.get(0); + PowerpointRun lastRun = affectedRuns.get(affectedRuns.size() - 1); + replacement.setRPr(firstRun.run() + .getRPr()); + // remove the expression from first and last run + firstRun.replace(matchStartIndex, matchEndIndex, ""); + lastRun.replace(matchStartIndex, matchEndIndex, ""); + + // remove all runs between first and last + for (PowerpointRun run : affectedRuns) { + if (!Objects.equals(run, firstRun) && !Objects.equals(run, + lastRun)) { + runs + .remove(run.run()); + } + } + + // add replacement run between first and last run + runs + .add(firstRun.indexInParent() + 1, replacement); + + recalculateRuns(); + } + } + + /** + * Returns the aggregated text over all runs. + * + * @return the text of all runs. + */ + @Override + public String asString() { + return runs.stream() + .map(PowerpointRun::run) + .map(CTRegularTextRun::getT) + .collect(joining()) + "\n"; + } + + private List getAffectedRuns(int startIndex, int endIndex) { + return runs.stream() + .filter(run -> run.isTouchedByRange(startIndex, endIndex)) + .toList(); + } + + private static CTRegularTextRun create( + String text, + CTTextParagraph parentParagraph + ) { + CTRegularTextRun run = new CTRegularTextRun(); + run.setT(text); + applyParagraphStyle(parentParagraph, run); + return run; + } + + private static void applyParagraphStyle( + CTTextParagraph p, + CTRegularTextRun run + ) { + var properties = p.getPPr(); + if (properties == null) return; + + var textCharacterProperties = properties.getDefRPr(); + if (textCharacterProperties == null) return; + + run.setRPr(apply(textCharacterProperties)); + } + + private static CTTextCharacterProperties apply( + CTTextCharacterProperties source + ) { + return apply(source, new CTTextCharacterProperties()); + } + + private static CTTextCharacterProperties apply( + CTTextCharacterProperties source, + CTTextCharacterProperties destination + ) { + if (source.getAltLang() != null) + destination.setAltLang(source.getAltLang()); + if (source.getBaseline() != null) + destination.setBaseline(source.getBaseline()); + if (source.getBmk() != null) + destination.setBmk(source.getBmk()); + if (source.getBlipFill() != null) + destination.setBlipFill(source.getBlipFill()); + if (source.getCap() != null) + destination.setCap(source.getCap()); + if (source.getCs() != null) + destination.setCs(source.getCs()); + if (source.getGradFill() != null) + destination.setGradFill(source.getGradFill()); + if (source.getGrpFill() != null) + destination.setGrpFill(source.getGrpFill()); + if (source.getHighlight() != null) + destination.setHighlight(source.getHighlight()); + if (source.getHlinkClick() != null) + destination.setHlinkClick(source.getHlinkClick()); + if (source.getHlinkMouseOver() != null) + destination.setHlinkMouseOver(source.getHlinkMouseOver()); + if (source.getKern() != null) + destination.setKern(source.getKern()); + if (source.getLang() != null) + destination.setLang(source.getLang()); + if (source.getLn() != null) + destination.setLn(source.getLn()); + if (source.getLatin() != null) + destination.setLatin(source.getLatin()); + if (source.getNoFill() != null) + destination.setNoFill(source.getNoFill()); + if (source.getPattFill() != null) + destination.setPattFill(source.getPattFill()); + if (source.getSpc() != null) + destination.setSpc(source.getSpc()); + if (source.getSym() != null) + destination.setSym(source.getSym()); + if (source.getStrike() != null) + destination.setStrike(source.getStrike()); + if (source.getSz() != null) + destination.setSz(source.getSz()); + if (source.getSmtId() != 0) + destination.setSmtId(source.getSmtId()); + if (source.getU() != null) + destination.setU(source.getU()); + if (source.getUFill() != null) + destination.setUFill(source.getUFill()); + if (source.getUFillTx() != null) + destination.setUFillTx(source.getUFillTx()); + if (source.getULn() != null) + destination.setULn(source.getULn()); + if (source.getULnTx() != null) + destination.setULnTx(source.getULnTx()); + if (source.getULnTx() != null) + destination.setULnTx(source.getULnTx()); + return destination; + } + + /** + * Returns the list of runs that are aggregated. Depending on what modifications were done to the aggregated text, + * this list may not return the same runs initially added to the aggregator. + * + * @return the list of aggregated runs. + */ + private List getRuns() { + return runs.stream() + .map(PowerpointRun::run) + .toList(); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return asString(); + } + +} diff --git a/src/main/java/pro/verron/docxstamper/core/PowerpointRun.java b/src/main/java/pro/verron/docxstamper/core/PowerpointRun.java new file mode 100644 index 00000000..fdf4879e --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/core/PowerpointRun.java @@ -0,0 +1,35 @@ +package pro.verron.docxstamper.core; + +import org.docx4j.dml.CTRegularTextRun; + +public record PowerpointRun(int startIndex, int endIndex, int indexInParent, CTRegularTextRun run) { + public boolean isTouchedByRange(int globalStartIndex, int globalEndIndex) { + return ((startIndex >= globalStartIndex) && (startIndex <= globalEndIndex)) + || ((endIndex >= globalStartIndex) && (endIndex <= globalEndIndex)) + || ((startIndex <= globalStartIndex) && (endIndex >= globalEndIndex)); + } + + public void replace( + int globalStartIndex, + int globalEndIndex, + String replacement + ) { + int localStartIndex = globalIndexToLocalIndex(globalStartIndex); + int localEndIndex = globalIndexToLocalIndex(globalEndIndex); + var source = run.getT(); + var target = source.substring(0, localStartIndex) + + replacement + + source.substring(localEndIndex + 1); + run.setT(target); + } + + private int globalIndexToLocalIndex(int globalIndex) { + if (globalIndex < startIndex) return 0; + else if (globalIndex > endIndex) return lastIndex(run.getT()); + else return globalIndex - startIndex; + } + + private int lastIndex(String string) { + return string.length() - 1; + } +} diff --git a/src/main/java/pro/verron/docxstamper/core/PowerpointStamper.java b/src/main/java/pro/verron/docxstamper/core/PowerpointStamper.java new file mode 100644 index 00000000..eb1f63f5 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/core/PowerpointStamper.java @@ -0,0 +1,54 @@ +package pro.verron.docxstamper.core; + +import org.docx4j.dml.CTRegularTextRun; +import org.docx4j.dml.CTTextParagraph; +import org.docx4j.openpackaging.exceptions.Docx4JException; +import org.docx4j.openpackaging.packages.PresentationMLPackage; +import org.springframework.expression.spel.SpelParserConfiguration; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import pro.verron.docxstamper.api.OfficeStamper; +import pro.verron.docxstamper.api.OfficeStamperException; + +import java.io.OutputStream; +import java.util.List; + +public class PowerpointStamper + implements OfficeStamper { + + @Override + public void stamp( + PresentationMLPackage template, + Object context, + OutputStream outputStream + ) + throws OfficeStamperException { + Class ctTextParagraphClass = CTTextParagraph.class; + List ctTextParagraphs = PowerpointCollector.collect(template, + ctTextParagraphClass); + for (CTTextParagraph paragraph : ctTextParagraphs) { + PowerpointParagraph paragraph1 = new PowerpointParagraph( + paragraph); + String string = paragraph1.asString(paragraph); + for (var variable : Placeholders.findVariables(string)) { + var replacement = new CTRegularTextRun(); + var evaluationContext = new StandardEvaluationContext(context); + var parserConfiguration = new SpelParserConfiguration(); + var parser = new SpelExpressionParser(parserConfiguration); + var expression = parser.parseExpression(variable.content()); + var value = expression.getValue(evaluationContext); + + replacement.setT((String) value); + paragraph1.replace(variable, replacement); + } + + } + try { + template.save(outputStream); + } catch (Docx4JException e) { + throw new RuntimeException(e); + } + } + + +} diff --git a/src/main/java/pro/verron/docxstamper/core/PowerpointVisitor.java b/src/main/java/pro/verron/docxstamper/core/PowerpointVisitor.java new file mode 100644 index 00000000..3d8ce658 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/core/PowerpointVisitor.java @@ -0,0 +1,95 @@ +package pro.verron.docxstamper.core; + +import org.docx4j.dml.CTRegularTextRun; +import org.docx4j.dml.CTTextBody; +import org.docx4j.dml.CTTextParagraph; +import org.docx4j.openpackaging.packages.PresentationMLPackage; +import org.docx4j.openpackaging.parts.PresentationML.MainPresentationPart; +import org.docx4j.openpackaging.parts.PresentationML.SlidePart; +import org.pptx4j.Pptx4jException; +import org.pptx4j.pml.*; +import pro.verron.docxstamper.api.OfficeStamperException; + +import java.util.List; + +abstract class PowerpointVisitor { + void visit(Presentation.SldSz element) { + // Do Nothing + } + + public final void visit(Object object) { + before(object); + if (object instanceof PresentationMLPackage element) visit(element); + else if (object instanceof MainPresentationPart element) + visit(element); + else if (object instanceof List elements) visit(elements); + else if (object instanceof SlidePart element) visit(element); + else if (object instanceof Sld element) visit(element); + else if (object instanceof CommonSlideData element) visit(element); + else if (object instanceof GroupShape element) visit(element); + else if (object instanceof Shape element) visit(element); + else if (object instanceof CTTextBody element) visit(element); + else if (object instanceof CTTextParagraph element) visit(element); + else if (object instanceof CTRegularTextRun element) visit(element); + else if (object instanceof Presentation.SldSz element) + visit(element); + else { + System.out.println(object); + throw new UnsupportedOperationException( + "At least one of the methods needs to be subclassed"); + } + } + + protected abstract void before(Object object); + + void visit(List elements) { + for (Object element : elements) { + visit(element); + } + } + + void visit(SlidePart element) { + visit(element.getJaxbElement()); + } + + void visit(CTTextBody element) { + visit(element.getP()); + } + + void visit(CTRegularTextRun element) { + // Do nothing + } + + void visit(CTTextParagraph element) { + visit(element.getEGTextRun()); + } + + void visit(Shape element) { + visit(element.getTxBody()); + } + + void visit(CommonSlideData element) { + visit(element.getSpTree()); + } + + void visit(GroupShape element) { + visit(element.getSpOrGrpSpOrGraphicFrame()); + } + + void visit(Sld element) { + visit(element.getCSld()); + } + + + void visit(PresentationMLPackage element) { + visit(element.getMainPresentationPart()); + } + + void visit(MainPresentationPart element) { + try { + visit(element.getSlideParts()); + } catch (Pptx4jException e) { + throw new OfficeStamperException(e); + } + } +} diff --git a/src/main/java/pro/verron/docxstamper/core/StandardParagraph.java b/src/main/java/pro/verron/docxstamper/core/StandardParagraph.java index bc30e81d..876b362c 100644 --- a/src/main/java/pro/verron/docxstamper/core/StandardParagraph.java +++ b/src/main/java/pro/verron/docxstamper/core/StandardParagraph.java @@ -27,7 +27,7 @@ * @since 1.0.8 */ public class StandardParagraph - implements Paragraph { + implements Paragraph { private final List runs = new ArrayList<>(); private final P paragraph; private int currentPosition = 0; diff --git a/src/main/java/pro/verron/docxstamper/preset/OfficeStamperConfigurations.java b/src/main/java/pro/verron/docxstamper/preset/OfficeStamperConfigurations.java index 0db663e8..652fe9c9 100644 --- a/src/main/java/pro/verron/docxstamper/preset/OfficeStamperConfigurations.java +++ b/src/main/java/pro/verron/docxstamper/preset/OfficeStamperConfigurations.java @@ -1,18 +1,217 @@ package pro.verron.docxstamper.preset; +import org.docx4j.openpackaging.packages.WordprocessingMLPackage; +import org.springframework.expression.spel.SpelParserConfiguration; import org.wickedsource.docxstamper.preprocessor.MergeSameStyleRuns; import org.wickedsource.docxstamper.preprocessor.RemoveProofErrors; -import pro.verron.docxstamper.api.OfficeStamperConfiguration; +import pro.verron.docxstamper.api.*; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; public class OfficeStamperConfigurations { public static OfficeStamperConfiguration standard() { return new org.wickedsource.docxstamper.DocxStamperConfiguration(); } - static OfficeStamperConfiguration standardWithPreprocessing() { + public static OfficeStamperConfiguration standardWithPreprocessing() { var configuration = standard(); configuration.addPreprocessor(new RemoveProofErrors()); configuration.addPreprocessor(new MergeSameStyleRuns()); return configuration; } + + public static OfficeStamperConfiguration powerpoint() { + return new OfficeStamperConfiguration() { + @Override + public Optional nullReplacementValue() { + return Optional.empty(); + } + + @Override + public boolean isFailOnUnresolvedExpression() { + return false; + } + + @Override + public OfficeStamperConfiguration setFailOnUnresolvedExpression( + boolean failOnUnresolvedExpression + ) { + return null; + } + + @Override + public OfficeStamperConfiguration nullValuesDefault(String nullValuesDefault) { + return null; + } + + @Override + public OfficeStamperConfiguration replaceNullValues(boolean replaceNullValues) { + return null; + } + + @Override + public OfficeStamperConfiguration unresolvedExpressionsDefaultValue( + String unresolvedExpressionsDefaultValue + ) { + return null; + } + + @Override + public OfficeStamperConfiguration replaceUnresolvedExpressions( + boolean replaceUnresolvedExpressions + ) { + return null; + } + + @Override + public OfficeStamperConfiguration leaveEmptyOnExpressionError( + boolean leaveEmpty + ) { + return null; + } + + @Override + public OfficeStamperConfiguration addTypeResolver( + Class resolvedType, + ITypeResolver resolver + ) { + return null; + } + + @Override + public OfficeStamperConfiguration exposeInterfaceToExpressionLanguage( + Class interfaceClass, + Object implementation + ) { + return null; + } + + @Override + public OfficeStamperConfiguration addCommentProcessor( + Class interfaceClass, + Function commentProcessorFactory + ) { + return null; + } + + @Override + public OfficeStamper build() { + return null; + } + + @Override + public void addPreprocessor(PreProcessor preprocessor) { + + } + + @Override + public boolean isReplaceUnresolvedExpressions() { + return false; + } + + @Override + public boolean isLeaveEmptyOnExpressionError() { + return false; + } + + @Override + public String getUnresolvedExpressionsDefaultValue() { + return null; + } + + @Override + public String getLineBreakPlaceholder() { + return null; + } + + @Override + public OfficeStamperConfiguration setLineBreakPlaceholder(String lineBreakPlaceholder) { + return null; + } + + @Override + public EvaluationContextConfigurer getEvaluationContextConfigurer() { + return null; + } + + @Override + public OfficeStamperConfiguration setEvaluationContextConfigurer( + EvaluationContextConfigurer evaluationContextConfigurer + ) { + return null; + } + + @Override + public SpelParserConfiguration getSpelParserConfiguration() { + return null; + } + + @Override + public OfficeStamperConfiguration setSpelParserConfiguration( + SpelParserConfiguration spelParserConfiguration + ) { + return null; + } + + @Override + public Map, Object> getExpressionFunctions() { + return null; + } + + @Override + public Map, ITypeResolver> getTypeResolvers() { + return null; + } + + @Override + public ITypeResolver getDefaultTypeResolver() { + return null; + } + + @Override + public OfficeStamperConfiguration setDefaultTypeResolver( + ITypeResolver defaultResolver + ) { + return null; + } + + @Override + public Map, Function> getCommentProcessors() { + return null; + } + + @Override + public boolean isReplaceNullValues() { + return false; + } + + @Override + public String getNullValuesDefault() { + return null; + } + + @Override + public List getPreprocessors() { + return null; + } + + @Override + public List getResolvers() { + return null; + } + + @Override + public OfficeStamperConfiguration setResolvers(List resolvers) { + return null; + } + + @Override + public OfficeStamperConfiguration addResolver(ObjectResolver resolver) { + return null; + } + }; + } } diff --git a/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java b/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java index cb02e301..6a8f226b 100644 --- a/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java +++ b/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java @@ -1,11 +1,13 @@ package pro.verron.docxstamper.preset; +import org.docx4j.openpackaging.packages.PresentationMLPackage; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.wickedsource.docxstamper.DocxStamper; import org.wickedsource.docxstamper.preprocessor.MergeSameStyleRuns; import org.wickedsource.docxstamper.preprocessor.RemoveProofErrors; import pro.verron.docxstamper.api.OfficeStamper; import pro.verron.docxstamper.api.OfficeStamperConfiguration; +import pro.verron.docxstamper.core.PowerpointStamper; /** * Main class of the docx-stamper library. @@ -31,8 +33,11 @@ public static OfficeStamper docxStamper( * * @return a new DocxStamper */ - public OfficeStamper docxStamper() { + public static OfficeStamper docxStamper() { return new DocxStamper<>(OfficeStamperConfigurations.standardWithPreprocessing()); } + public static OfficeStamper pptxStamper() { + return new PowerpointStamper(); + } } diff --git a/src/test/java/pro/verron/docxstamper/test/BasicPowerpointTest.java b/src/test/java/pro/verron/docxstamper/test/BasicPowerpointTest.java new file mode 100644 index 00000000..12e45e04 --- /dev/null +++ b/src/test/java/pro/verron/docxstamper/test/BasicPowerpointTest.java @@ -0,0 +1,35 @@ +package pro.verron.docxstamper.test; + +import org.docx4j.openpackaging.exceptions.Docx4JException; +import org.docx4j.openpackaging.packages.PresentationMLPackage; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import pro.verron.docxstamper.preset.OfficeStampers; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; + +public class BasicPowerpointTest { + @Test + public void testStamper() + throws IOException, Docx4JException { + var stamper = OfficeStampers.pptxStamper(); + var templatePath = Path.of("test", "sources", "powerpoint-base.pptx"); + var templateStream = Files.newInputStream(templatePath); + record Person(String name) {} + var context = new Person("Bart"); + PresentationMLPackage load = PresentationMLPackage.load(templateStream); + OutputStream outputStream = IOStreams.getOutputStream(); + stamper.stamp(load, context, outputStream); + InputStream inputStream = IOStreams.getInputStream(outputStream); + PresentationMLPackage presentationMLPackage = PresentationMLPackage.load(inputStream); + Assertions.assertEquals(""" + Hello + Bart + """, + Stringifiers.stringifyPowerpoint(presentationMLPackage)); + } +} diff --git a/src/test/java/pro/verron/docxstamper/test/Stringifiers.java b/src/test/java/pro/verron/docxstamper/test/Stringifiers.java new file mode 100644 index 00000000..8b334f35 --- /dev/null +++ b/src/test/java/pro/verron/docxstamper/test/Stringifiers.java @@ -0,0 +1,21 @@ +package pro.verron.docxstamper.test; + +import org.docx4j.dml.CTTextParagraph; +import org.docx4j.openpackaging.packages.PresentationMLPackage; +import pro.verron.docxstamper.core.PowerpointCollector; +import pro.verron.docxstamper.core.PowerpointParagraph; + +public class Stringifiers { + public static String stringifyPowerpoint(PresentationMLPackage presentation) { + var collector = new PowerpointCollector<>(CTTextParagraph.class); + collector.visit(presentation); + var collected = collector.collect(); + + var powerpoint = new StringBuilder(); + for (CTTextParagraph paragraph : collected) { + powerpoint.append(new PowerpointParagraph(paragraph).asString()); + } + return powerpoint.toString(); + } + +} diff --git a/test/sources/powerpoint-base.pptx b/test/sources/powerpoint-base.pptx new file mode 100644 index 0000000000000000000000000000000000000000..9cb051adac63dac64bf69b1ff126f441a321f2cb GIT binary patch literal 33325 zcmeFYQ+TD@)-4>{nXy^1XHYRJwq3DpyJA&r+eyV{C8^l9ZR=#My$`?jKl{Hw=e?NY z9dnF%F~-w->#g_Rro0q51ONmI1O@~Igal+`6#K3c6a+*R0R#jc1O`k?*w)6$*v9F* zvb&wJqYi_cwH0w51Q=Bg2-xTT|M&b~d;_D&T5`QifWZx!S0t(Lh_pGRxKPNtS?#f* zL^(FlB`(%8m_fD|OOOFU>0q^tQsd)K9~0GQ5AS|k|$$_Em zcZ4)DXzGJTy+Ls(KdIHH&9k%WJ(LjH1sdv6alRJYQZ-zmD4GZt3K|quOaH+SHW2_u z@GpUwb!b{os!Kpmt7z{FE?06N{`zUx^9EsyMNWhZMczyg_e&Rb%iJXP%b{cxsD3h= zv}KRsK9k%aiw0?Vf-(us4KDZYa+ZEi8j^;Icc9`!K+Mb~L;wTpd1|5hw+0twyqb~` z%f2yLT6vYO2}5yZ9!Ui>hP1+MG3=tqktO2!9(8|?>=b9ty9mLvNhA1mZstU`!L3En zSo2mmeA9^50SN=$mcY2V{NMr!RkefP={YZ(eTp=#&foxw8G~ z#PFY9{@uaY%8`-buh0MI-TxQ6^#5}7intX?aAuU?3&;(UQE!eF8q#SiTam-ep=S_7 zmNvXKeDR{`&W_woeSP|Xj?qzmq78Bl+j|?pMM52-ZckmAVC+JV`Ub6=4H0G&F-txN zCzKoL~{pWbOZIDnOgOBT%f;wXZVmR}23gnkEuRN&6y_`mcPQ%`KUu*L+JfTVptvQapu7-)RHuC<#&(4;inu9=O?% zHjnMPE}TZ@E*Mh6ebmRN%%>7@ajdH@i4VV}iGQL@Pv9l zgv|Fin-Wfec%&RN{D)*G;Q^nP_m{AvwGW}uk52>ouck#>LH^P5xgrG(0)p{x({i*j zH!^l){Ci;j%eF>U)@;(50G-+Ot^)HmI^8HjdDAo~izU&)=u`AZKoWQk{SW}Nvk!7}ODXk|d52+T!R4mF-4Q8af_kecfS<$B1tum1 ztT_nP&!4S3HX3lOYWuFQ*%epKw)@Q_R^r0jo&9iT9kek8JbZb4%SpOlhRkw9E0xM>C-?kf6< zU7|Zx={J2D^{&*pLl^xsd%zBpK{)gpq^DgRJf$!<1am|W^izsN@^+&azqf^6sgG^N7qc6TNNyQO7F@l@q) zzta)W&t&8C2mg0J74d?yeM~6d9>iCB(s=C^bA(D{IbcfR$sx8tC{xKXqpjVgNty;N za>-RNoPX|p+`qb~)vBFb$o``DP@IPuiUVM$O{@N416vOtEa%n(6qFV;LP6e&*NihV zsxp=shkl!-5u+=@Mw<-YJm)H|8<15rAV_ml)Y=$EDYR{l-wUydWt?^zeq#c^R-jx_ z274`Pg;Kpmniy_$u9Wnu*)t8H%rNW86!W-eXE*$DiwFe@O&ez3EhkL3A4FV9^9sE< z{GB5`V8|rIEe@-nG`Iy`=NCzu8e{^gvS;hSe?Gm%JY0nEXX+l${#W+?znor1-_gm~;okw|Uz5MX z$f?$v&B|A_&hL5~f|F-L4OEK0qM6_)7gqlDUsjxm{C=8g;b|yRFMl}_^yeFCSzbRo zJKwvPc_!yBAlG`TjnA<5BWHqBpS2S~JV<{EE|^~6*P(;A9&?LV+Y=G1mew&e)--nK z%0Be{h{evT0tO`E(3i!@+)B+K@)Nv0$yJ}&D;e3ElVLCDMb_cWgmt3i5ypthwCYJM zx0hL{3T;$uOVMm5O^D$Xcl9og7#O7%v1(4Y&$b;rd1qCd{NgcINUTb#0GttLU?}^& zjgeNT`_w=4<m6V$HG=ZWE zgxL==UprH-3flub*=kVa+Y`FTs131%q_p%~6N@6g+f2z|UC}S+zeDEtNbOZA<%B?E=bKS^-Z4_i>$ zi>$T=OUnjLbMc8TTXdXrPul;1@+1(2i^K2xK6Q1P1*LA`TwO#C9}1|WNGnr9(uYa# zPnlSA9Dj6oC=v#CAxm7m_1RQSf9|^)L+|qpF!l#EV1&X!;5ZI1r;N9D;X#zDDo9p; z>wrW46c}Cgoe+95@Db5Vxkk47OgXCZ!1l*lA7M~UFFH4S-Dx?fpETZ5v~uhasnt?r zVD+ZdAkemdqC(T6j(Lhp>IYbOm40xgMRlH)1%2t@Hfc$nRH-|Z8VvX*z|BYDO0HG4 zojN`2oJ?qQO=t~erC~D|*>k+=Hk7mi4kM&qT0bK!3$DtFnk2B8R$=UVJnufSuH3A@ zw3#j?&6VCaAtg`{GBJUDNA$YWIfByzo`+*(=}V=afWze?CC^?7edF|A{OWKm0_|KXxMqmIiWuwIVxYk|9VcffjUAX~y(t790GD7p z9CyN~x_IVm->-`G)a1*Ms=99}s`h|A*XlWo-2NvW8mWB9?xV(6OQDB|lddJ?=H`0m zI9i`bRKSP>l(|M=go-#Un+g(A0w5=&M}$1^6IJf5RR1a090iX;Vb^Vj{F*2`fCbcY zLyieubMz~XqW^Y1r>+?88zZ2)H2@cEOMK(<}`l+ z^n1)&^mXlxQpwPT3$%>VS6Ey6{M4y_q5mm$ngrTBlBL>SXnD+XjgW)SI*qeki_8$M zEDE_D<1w=#0Hmx|v2kkm+;dII&{`g^W_+P)E5Yt}!W|a(T>z6+DMUh`{ry>*R;Sw_ z0Vr3xEYP>K55c1T3}9T{rS5mzRYk8bGId-dqkWY!|2fNY{)!rHW*VMxd1d zgKC@%{bnyzArneUG0%2drGmw`^gs!@_w9 z52DlXhprw(sKBvEC%gniB3&&riT_ zQMv|qaZyO;6wS%8x+$yf$FmR^E35%(hc>RcWs% zPyDdOm&qhVv~w6j9%Fe7skdxd1_auity9f9sx+T3roMseG>9(dTOvAP@OV0138+m? z5$do^I-gHAK)0R~w2lj?WyYUQIYYO;6SS_Qz$X@YCRBqgEJUYwN5BuzM_G&*&St)# zmGu8YzCFEF604=Jq>(kgZAN%(`I?eUg!ShfvlX)&S)Nf&)tJVp3%7-*a;CAj@Qb-v zTxSc#aIO$BKccqEn%z;+gVkLX%}RU*s7{k;7Ar&7+-h=bTW*M^g=F|Q(;Dg2Ltl;e zJ}MD4grf!|(KcJ~v4-gSqu-s0`_&#@sy{BE08)QqD3lsbcG@BMpo?)kHjjuY(o``J z;Wj`j3Ie8^@5U1j02Ni{8bCWsO%o7WWUz&IA|s2;2&bQu#=1juP%zVwdx+@i0nUwZ zf_eSuZ!gIkT1UkAbPeWik#0wyqW-Uc%deFH-oqYM@?pO z4c(#Nt{EJdK7bw~f@yJBMi0^6UKNyuiUn`C;CJ?W6^zY4=srO^KyvVu|0A=a!bC{y z>mDnW!9A1-y30;FVf^Ic_v-)m7i*xqohzEYey@9i4EE>54~?;g1`@?Y#J*vFFTa8A zW*y)s*)jqg!W>_)%2pgxJ+#9hchZ$`-L?x5BW+wbCVn^3sKq!DQix}z$Jp{e%YU~+ zdS7yvpXA=!%qD2FIQGk3J(~^%cd7$mM~;G*sfSd*hDgc?h4rqWBk$R zyJ?~e#m-qtGW1FNEr+96FJ14~?Q180UWs{agV#YKTUVr#_ggPkItKDa=JO=mOl4>- z+!`&667TJ3<*-_$H$Yuf7($J&z9nKda787%;%ro?exuBz#G_GoAPWT;FAKk}wVJiz zRCd5?(td+x^0?iLeZIl0JB#Q96y>%JBeLl^60uA~|5+`Wfy2!swN9@(jEG`#(C-GJ zcFL~eO%f{6?0AxC6g#sl4I#AAmC7H&n1#@1D1uhv@Yb?*GPXBhTUt!9*7|iQ7MKCD1 zFoB9#r=CU>7X;yw!IU6?$V{t86`C4?CbX8qDIrevQ6g&CnJkgp^2D1B41NOYmXpTI zEL1$3YADHor8+|;7_cjp<-K-G01SU?@7BQNQ#p|tK~_b!>7G!NAdZixk;Z`t#w7m+yhe}tgWMj#wOKA8*=VY=j;tvkN8T#91%!lCL=M43|={ZZ7 zxJuBpSj`;w^Is{uVOQ3BT*AtQ+^`$LGHRYM(^1sY|Mb;hYzCd3>TyT2T{Tm^}x3 zI%4WPM59C|=czwge7phpf+~k^v_&4oDmtXFX&3!UjzDnkNfHr2bg|)XYjM0_-jc@a zHQUW0qn*uT>#XUWpI8?_Rx>^yvmGj0qoBY25=y})22?w! zNj`8yJW!sy0MNzAm{Rdp53ld!EVzH^*Hi~2rPWk66GM%8W_HOMm(}}xYuq5?eOZ~t zi)=(`iPKBZf(ztTcw^?a({F0{i6}yxrU%W;kATv|vrrp)hWd1C( zzxjbrexBbmNRhQ6>cy-4oo__cKKsJ!dP#0BfE53m0Drgvbf+EKMnJ@XzRPbIWKN2N z_aHK(R3fCgC>m^JkC+pY#pvG9X_%knuzJISuT=zwLBbpM+kV^$+wfnG|A4v3hn`nK z-QLY?gnYc;-OcdFZ1T6Jd?1gWR9VJ*1a$9^JAniAl87c_ZAR*EM16>EU7X7NcipoSyYR9^byB2hF;qU9z0ici8Cf0E5#Eb@)P z;z7r9Z7e>UMpY{eG!IV`Q}4e22(u@>2CV)NARxt`dEoz+==}rCviw~yR2#8bV@LTn z%zDIn;M9V5r~~Y*I9&Mse!5vVc%dtmR`g@uZ^IxaGo{}zryCG%ejMyr|ZOPyR}k=ju?MQBU`ChFVf=jO9C1`q6?*CHFkmj9fCq%2I_$zUI)6+9jZW9sFJWEcS)~(~Mp%0sT1AMGgJ< zHFrg&G3d$n2pVN2yQZyX_zY@18i}vbZ-NY{QF+Tie)(fcU0K+?N#TB37)b{-lH+YA zSMapf#V+Mv1ir;6+P|(HTS`W*Y?+spe;fweP<#jIuu2&!V8Ax z({6ddEpt=3dJrAU4bo3jhz?f9r#3?uXy|k?wob0ctm~5H(xsXR$--DgSx_qzsj8ku zbEL;UXT<=!>Kh$f#a0kPH+nG-?j@g)-{fo|T27z@^^qh|-ZQbhhQU3=csqG6s<@aD z(NsGi6VJO4)n|(r=#E8>7lNWk1u1U;ONHFw?!Vsa#T|WvH}zUoUdYC^sh>;xbp@)s zX6wm|ssr>Y)_S#W>WY*Ozi^TwjD0QRCFslz>OkTW(Mv!`>7S@aGEZnTkJd>z zunE%UFQn?RM{vg0n~=&U?!TI`9V!fhANLgKJx+v^p4)dq;*+$m+N{c%`mK?*HTn>{ z112g|D}?7Z>!!C5IQ)`KioO8u&+`g?Mye!rrF+PmdoIVp-{ zj$tVr-e9u5X%7|sbj;fRo#@E@fdJHdK#HO#E7Kv6XTg!!NeCALdy7p91<4B8-9gXi%j=5z1}dK2RqnV5}pT}z*fC;xd- zq#uA5A}>-$CBXcHH_0O zx?pYV(IZ~RhU`J{;`-i(vJD7WJkZZiWzUnd*rH#r3}eEdl5JA|C!2iBHh!FFA* z=NIm4pW?kFLK5mvIHTLF4f+^q!_#oGQXR#k?yuq_iW(b*kW>~JtY3#}>8CJtG#U=1 zUKWyZuB|QQ-L>|e7s3m@si9tmuVVOl&SQdGd4?I=qUwl6x$*a;JS&xb>(vnkG})dm zOHRJQPpD>EIY2gDdnx>Rbq!sch2Q2p&l@hE&wAY>d0(?o!5}>fmm$*xLjBAe$JivA zSK%L>dKS*RQ$#@d?dFRlp>rR5MjW2~O?NqoIYBF>_nvuIH)F1FZXZ#)y&?giN=?T! zZ$EmI>r4G(GT-GT{&eE`V7fWeCR?hU#yMqpO;;M3lGSm=W=zk1?9GdCUkr5z7| zuT_0nV5RU~=a9=jx56NU?*7-n^Acj?z}Jo*4`#-i=DLz`3y25fuST+_6~PQ~J@zam zI8&mkQZLp?KSPUggmEHK97%)o5+JF_X63??2ZCBJ>N#9u;HVK;yApo&*1SNL%cu<5 z9I75GOabG0OcFtJQ)GfH5^N>#Q6`XicR9lX+ZTuRoDuTr;JGCFP(p^=NX9d+FNu9t`o(6@#@NBrhOTCA4!9gQfoO6`A%Dcnf+$n(bw5Ej4|9_9n}xg@)? zliaK2jNbAQH=QX){qaY)!{f5ivQ~DEG$z!A>E(Uk>1@OmxspoxZ=9G5=Pp*li+g*_ z8_8^2WL9#|f=Ex1g-zmSkENG@a%Ne(BSIU$)T$*xyIS8EJK%3vhLyg78m-8Fq~3D+ z3;1d%uB|<_%jfweI2p-I@bqay3G=~=){ODh;}IT1$IyvIUj7?9S8b327$ zoUB=?Hf22+n2>mqL|m)8665kg zzS}yK24w&r8D;K#r#2rb);ppeU1q#qiMhwTHTJwYl*^XkBKVbI3lsqMqw-*x7;wEz z;IIX-2B@xJm@m4=zg-#qa~b3~#-eD5UG32Hv-)8RVM#SG1&2#Rys`{}b|!D8hwi8b z+TzXp7K%Vrk@@`iIzDc0FZe%)6YdQDeU=Xe_Jy@b$~zH-&A7!F`5+wmWC%hyR!&1! zS)2$m{z&t(xf#RzLJz%brn}p<3!!@*<9E6KadP4rJNfNil5s9}q8j)>>&|m!hoYMh zOZx2nw_lY%|AOd2ld^?re3=KzuMbbivpfGxF&o~eWp_U*<|F!lhG@3G&z!8#(oWwwTfeS?x#myKX#?UilosYDgeI@=E)X+blsKqKqnpU9zD=|zenHM z$ld45t@g^#F9(PkjZgbEqXb?A3U%>6LAp*9(a396{o8`%dUT*W=j~vCO1;L_-7qveb}gABh$tCj69v?!Z7UG;QtEkT!}>S)3pJade{ zd8RCKbuGQ@jb%lqG3bGB#BoK@W>ryOl2bG-b#tNXQ2&)%tdvN8$^98eLSFwwDRjv( z=!6VU?5-{ZRj+F~_rl3A1B4j)i680l7H#dClQX#iEvYKRf@5dPXT6+z{z}(YIu$FbhzSU|EZR^@e z?P1wgw9VC-jW1p*z}4yGtmaAO#i4!RY_0zMEYI0Xu0d8f#l+?~puZ2BTq6%NnhJ_a zHWu$WwN-RO;Uw6`b^5D6=rhSZk->+o!2>^|OBHF2WKrzx`3daAgINkCNjGyL@sJ@_ zHxrp3aSCb54CkM&qX_S>TZz-1*WuYn^WXRSn-sqjQm$~0ux6kV2>=GW^VKtr?KZ)| z4AG1^olxP{8HiYLIW&M7s$cRqL-N^Bbc+&!Y%G;rmW&0A$gwBQ>ej&P#$82b?A4WP z9Q4yC6$c;KyCIp2)Xwd~BE1RO=DiK2_ZEAUAXpgzOg*e-@?j8)a9_}{000SY9?$Fj ztCj>a>gC>0(5}JOCjG%^o89FJ7||+(7dQI>kw|eZBkjDaFojCtuOZ$-5Xm(r$)Cu8 zRhS$j-;d|V^XzJQU+G`3nHu+NwC&E)Q=4(l&X_B2U<+zkUPpx6$0C8i!XHTr(6@C{2|v;1uTo?_TX1z9pLM_1vE>ykQTg&bdgW zQo5^@oh{W`m`=F(yl$Cy%^2{B!!!MHp`HdUmN z9&I*qr4-9tBh^HB$y%jPM3B>(8h)6>zt1_O(krXeKMN(UflQb191*|YGnKEvR<1)- z+9<;_6+Jw~w(j7XtaOy$$J}qIc-s1;YZTf43b5c=@qc2}Jt$S7gj9x5QTQSg*Pvql zZgKJ^O07eq#t*zZc{VomIIYrUS52}Asy(pcduL{6ZQA<-*vD!mk{&QRFd5(U9*wB8G$}YAGv3pzf=^B(j3Fh&H31s2zkTQ_jE%4?mn--%7!7|kJP5O# z8f(N8=V#3b9o71+UGsHa(->03To-y>Rtv%C*wULbjON4v*ogPe_T!G`yF_h4RqV8# zSW94-W0sW__u|(55NT1rJ!i+z9*B;uvzSjJ;mNt}rbT^{)t>c3EBAO}OX*z4Cyoy7`_LI{{0)gbbJ~3bX{}A zI8!nx(fa#xdd|{4q@w9{Ak##JPrt{yx zCog)h3_S`hp5SmBFM3>3FkjQn(~7Cch&8}j#8B2;$dq4L%7Sqfj&SD&Mn!61X_Ar^ zfU}{0=1WrGtX!@|Yl`;V)uVvDUrqHWeo_Xn^jcJ)CROXh{#>P7wAKgC!_ba*!-;a3 zoYMTY>kqKv_i9ERt={LtNtXX!bThg!t|e9W68i6;JL1XB+y2h+gg_q8mB23xD#$+( zkq{A8xYeE7*yXAGS_F5v+kkE3l~^iErFBh*;CCk}dPht+xl~1H-x8RNZit1d&g>cQ z3HWP{yq_DA`t;)%#hM5+Zj)?e@K&N7VR-op>f$d}9NmG3l=1(O zeWyxLF`-So#mh+>=puCPDy_Q@uGzVACpcyFo(6IR8ROCUsCJ%pfqMSxIQQaaHADv9 zJ%rK1qKdN+l6dGpX z2yX!L?3>59FB@jvcTLPZQo5(z$ltJm8srcPYuBI*Fpv$!_lquG31zntx?1~exG%d8 zvL9Sj61>p1EaMFNjLs1{HgKQ0UfsP_5rfgn_({AAJa8jyJ~1SVyD>4j3PJ0~DD5`0 z+~!Sg@kLzlYe}_{sBzx}1&2KBd|!Lmzk3T#cT6qb(duJ@Gau5A?!Q5apx!M~<_zu2 zSc{d!yPl7k`2stpm5f0%j`S>gc9)rZnO9NKco?8fv3E3o;S$Q*L`?_Ox3m}hB7 zNji>mePHjurPk8TF1tPndh}x*qbwgs;y&M4T&u+|q>S-6WM!~UtiDBm5w=T6breMT zNJAnQ%0yC>+TASI4>YqIrzXZK_eafjaTkcC?OuhbJM0Ono zsrj7q*e8g?f)Ck&IgHuw%Wnr0pTj%y>!5Z~OvOxV`g92)(Ks@iI59%sFFtr3|8{lX z7<(#1f5r|!QB7W;N%9e%>wMyQBtpKr z%`8$!_A+>pDUzFV_I>R5RqK8S0+(Uu)O6bFR>=m1^5|RZEEv`18L<$=Fpq(@Qo62{ z>KdL?TGcVWuUm=Q_s&nj7j2cIoB3zSUdGn-Nm4845m9DtzOxcjy{oNChzpnH3}=d0 z51&upZ|wac;wO3yp&7n_bMxS!4gji{A`nN$jnh*uBklJ~zjp0I5d!w6Rq=06Z5 zE+~x3X>}rI8rQYV)XQ9$4mAg3z3p|AOZE^Ew3RhsXCth6M{lo%VD&xD&c9Gh?4{x& z$hw%qsTA5X;b`8oG1KGutgKDeb}@Hlw0XHVs79G@(M`_%0UN&Gw@hcYFH~Sg!VH-* zaD47wHi+l1StZ+Qdxy#t4$5yW%q&m!>gvrT18cvb3T3M@FYjxE}Z$P@G3 z46z(lA9<)SKAk~$ST-bQ5%V1n;}?zn?{d%}%oAs^KX~`JDfr;0MmTh?v|^T#f>>Ik zTh}VF2iPiCy<}$VZ8Gq`hA3rGO{z(#osUs=32`G_8?T)>c2o~sl8u?d2>YV|Ij~$R znLIhd%VRcr>2=k%!OK8p4TJyCPm7YpFQ?=z`Pvy1HP)Nfb}@2gQcQRb!~4&kMYZ5J zag+d1;AmPfL)+z1u;DcTUlCr&k!y&)!cJ~7)X1@7vQFfpJ`o8}0Ri*Kv9Q$(luXS( z&fPw;AR>Xv#Uy{R)-*^y7YTgBm>x)Rv%{V4sK9TcWFfJa9#yWQ96G=Nm1Hdn*(Q(J zORY}%jl+7(_KtG$=M>IH;jNM|S3h_IY$w|h)~d(0mxOn$+xCn6rf+pl==|lMa<(JbYD6ziib(veBS3Zqd?v}_axzgteh)+pn-C zeKhfM^o+WYZ174xT!rFOJJL zSQtV<`S7Ig`^xc;eIr=Sn9$r$z#xVD&w#=4FTjwp{j9qVUO+w*K&+Ybig#dYK$k;D zfK^%dn_oosuVLgtOWAX@eFDbGk8f3_YFnB%6yKxT5ZwA<22uOkB|5B4svzBfms==%c<+1 z0AQx!37yeP}dZ-6cfG@`g)H zR+BMyu7K>bbsmVaMzN(h@R&dIjoX&gmfZ2^>`GA8Ihv&IcPWC7Pj5N9H`(^W=u9oP zvR5BbjhO^Jsi_w%$?yES{2TcXjhLakEqxPK%wy8ZHma@ZdiT>pQEM40h8i5a7ILLA-?pDL5-_Dla(Yn?wnhYQCArz^1Wsq858Wbrj$ALMe0#QLMW5)P^& z48nyAT)&y4gfKYwk$-<$P|_kkaA#BDRKhB98NEi>u3+3iR$U16(mYcbL2Yg1ZXN@PP| zz48z4cq@0sZ0W^UhG5kfktRzk++97Vi!v$7rSxi596UPdOh<)rgtexrz70`Z)37u( zT)`gp)$1;$ce8dlZ9fzYR1zwOH-G|uODHBM$b0*wOhod5ZRIY^<^?w;Mlx%l<0&iC zV|#D>2YTm9LlZ~c!m=6={Y}2yV}kxQlT;vJ6uys6fE>{ff`^>S9yFLZUm|>GHNyV- z8m61$)oX!I(*FbO%TKIbjcEI5FG(dcu4s9D0O56=3IM|#TmN(|Q$!eiLskB676Ht6`p@4mtxnTbSj6D-amCaSNh-7{EBVu`{(%2bhIIAUnI9oQycIT!h_&X|) z(|CBJ8Oy5X{PL}kw)=+Ltxp1v>LvEuL({QT#-tu?Rl`Vawp26Zlu&&k&PlTkbef93 zWhBPub}SZp1JjLRk;@8b-Ingj0}kxrpEzzRY(yJ9uo^Wlq0E#|+Nc|_fRT+JAA?H{ zdDJQD-Qb1}5M*+)H_Eg-V1$n>& z)bP2tTA7oP6C)=^y{6kI*`9)7UMq+`ZwuZM`5p}GmoZq5JK%(U!E{-R@+tDA@MYK3 z5X?2wd1=)ldp7uE$x9S8XJS1Lt^H99XJg`S9cI1Fw4&OOPYK?zPS0)+^O-_@W24g{ zLTQp(VqJhfn=YS*UwY=%&JD6p9i>$dxbPfrHnS3MR}yo=wMnWuSSwgkqG zIgsb$_Z3B>SA~^JwFFl)7-J3BSX{AUlu8A5&s!fZy6OPjTEl9@h*^Yb4cKbPXuTwV znjBq2)wS`-^ilpMmLD_$q`N{|;2=oNQTZ#Tqn3-El1^U87s6e6(>2-}ih%?fAttT= zjs}4_!maSZ;|3#UYtsMxcAotd#xK^o?{neIdJz>Q_ZQ*{_H{RTsdjWGh~b}bV%Ukq*9Uyw0v1nO5V z^Q;QSV`S#TF3TIO%@{ESh1}9Usy>H}lYHauN0bL=C*oP97ovoBq!ScFQKZNkqpm-3 zJP_O&7I6`yy5Warh>!}lWM(bW2cM*i8u5dgF3dqsDDe5&Ix+U0a#y2{6NWB8!uP(c#IjMM}*#4 zCKs~Rde+Dw_Qt*6_0U^??wL^xthCS?KVJ!1(OAkFmv~{+*z^45>&|o&WZLG$%~Gu< z@_35r&+I!b`A*yL9cHJoTk|L8+)F34e`~wpkg+>>hY%ZhlGF)l499K_-Da=5&)jES z;C8SRvgzK{bxsk^MJZ0w$%Wi_qd<|@NE_*KZp?^xhM}2|j@Z$EA#RLjeqr$migmX9 z6(F|c{^c>fN`*ojBWoT|nSImTK5yz7bXO{?SH|3dinW__tXpJjh&lqSA>HsUFtuL3 zK8Y+=3~=@u2i~L}$A~UI@M?*905pd_rR>bf*h9uuCpfa$*rVPaZ)2KM@R!0^5lImW zfm*r)g!w&0T5gS{m&R(^I^mM@d}G_K7Im1iDQS1w6q|-KJsv>UM3xnhEHQOI*M5630ho$iZ zo&oAs=KMOn-wt-Wy1KmI9-tp%S|hWS2i?`CatJ3J=}uP&uMu!ba5)j8uQqFcN>Kf1 zIC~~fI4(u>_|AK2$8k41C&qA0qePUA=#4T-n#5?Ia)^{`#3@?7`un6oQg-igD=lKd z{>>!U2rN@D)%&g_Y5Fvm1BL7U%JSv^L!pW08j#TV86q_Pe}o9Ge}@R06;8k!GSdgK z+k_@L#ZS02fCmy>f%%jXpFrZFI(CQTaMetn0KakL(wK|Mr$)2Oh=H2AFQToE%76d=*m*A4=uOsC*eZv=~%-iVsXK^%A;jL&P@$gf! zy7+I;BRBgw^mRMCdAd$eddg>l4xFj3I*v@4jYrxE5-$}tB9I?r78;ZUtAB3HDdUm- zbTE|+(#GqhuPs=1M|7@FmgwZZsTW5mD(fJVe*5Vp@5?=r?jmC8B4}A)1}bW5n>?$- z#~k}oNF=%zt=&?}xq3JfnFaa1rC3aKAU1qd~i?b+6G zS+9#{y4QJ;Me!(!gZCPJePs?hTCpg_o4J^t7&LLHNgGzw(HD&TJfmBie3#PP7uC$4 z_nAkn9GP966zrK|O(JrGphb9rWg2$_YZ*v5#g0j?ir5yJP6UJ29IExPPg}c6_~;wa z2Ji!uLPkf_X6Q0GpH1rzm2a4*baQAix+k-iRZn4gGC0RBK1= zV&8gE74T0fQC8DQXqL3I^?qPQYddW}%ECt}-ib=I8aPc&m3pJn{cPZaiGF`BKOr9D0hXk-Nd&2jeiD6qu z`4HqRG z$QpWcD9FdpZ2&`s^D$*4m|Ur2l;EE4pG3we4h}cL>@|@6H3{v zExr!&uXk7Q$=W4w2?eSqPilOIb#fL3^Np4xVY=zo8PY`fK0*vA@4R_zF;Hma-JwC% z(B@%+-L!teNKxh;N=Nz?A1_N~QMM_F@puFFX7Z=h!LQ8L?5B_1StkjGR>HR*$8pQL z_lI$ABXd6M6jwT>D(@tE0H1)VfqQEMuNUY0aQ)A3Pd$v45~RZGQXd zMp$QnOP0-o$F=CV8tV{`a_|1Cn&WnIt=RwcBow|7A*=M*aT(co8X!bI*TC0CW*{pA zCnZzS(3}(e#($mpLii-Zz(Ua@>OEPLUGk3qC8tq_z(^Q_>XNE`7~0OVw;7c(7d>Ts z=frk?Sl90YE9^7~ANUxkR`8M6^M*EYPS&ZJpq5;xx@zh^J!`$bC#BCRcZVK5U^Q-l z{FDU=9uge_hQ(fJ?m?(Od!UQL(+ROm%B_*mRJI8%)(y>t+So^2z$QW-?XB+c2c?ND z(r%L(^9d;*@SR&hYxk1#$FU1A$M*X>-_!F#6XVBekMFJV>pfRhn+@XemG=S(=)>F= zuc+!F%kIEtZLQ2!Zr)-3Per-F%JgIZ@wM5RS)w4TV{Kou-}}Tl#)>6@NtWE~Cg3t1 zqVo+j+j4WKTbe^^5;q>D-@t@HhyBkj5|~~lokP=}9wlB6zyD+2Z`;zSp8na)-~3D) zNI!S{kH1C!gI<)G>2K6m8~zlG{&!tIZpqY;V@}v-r^?>2$y0UvSC|Fdw+^09S^h-W z|40i8$h#;B4WMPWqQn)FUB4daD#~SBr!OxuF<(~cq>iJnu&HagVA#s3?pF^&d@EuW zUskO@*KA~&Sk&WN`M3%xep{DUn>f7RM@1HbPCd&MnYOZx}-~_W78mwAQI9I z0@6rG3cL+o54i8S=lSmE{dLb>u;e2!zn?kAT5GP!9KSh6uS);{4s}>%cG_YqqAIB! zxd= z6JdcO9#@M&v>;+I@sO$1i&rR?C<9@$E*mohdJ%Kqv5l&JtctZR`EbG0Q;@-4J_DDOWMI%a%5`FD=_Fi? z-rb1(IB~!Z&d8-LM&5l4!74*Rb$5iCHTObEikl)Fj*gq=M>fd{1223?V&!h8?(bMT zm-JH8W4lC8qMIuu6x6rsp$&=IA6h@EI9xOLBz}W;pu9Fkc~s{v;32+o+BIBT{gl{A zR~aw7HLP66hUCTL2tMSKTjiUbq1d6?Ap;b>!MWBVq+&CTI!w8}bIf8QYu|uxK9w9% zoVuZNp^;fe5b}J4Bj>S-pa|3T1}>{HSVvHNq;wfUpJ5>+c#*h(PW{~wZ+(&Bp6`=^ zE+Ufnp%8Fv7R!P5L@8V5_&3J|SaQxyO9v?gcZcYt84-bVFmA?|d?4QX$_$*x9k zL+7O|Vq4J;pA)#-3c>^M2#>=jFh1hqe8)WSufW5Fc@m~9MMr?)(QRV`heS?rID3C~ zqNJLe@O5*;eEAmQ-Ly}{XVh8GQxI*EYH#gm_damOreP>^M|#D&-*QJ*`c>vk3hY-T zN7nlzrNSv*n3I8|LcO*jb_+NWB3(8CRE^f*k+gd1nGXU=?8XB964cNX>h0i;_vVUx zJkP%G&21qq3%RJD)yLGWjeSVwd_c%MnNVHz;&^qnnAe1Z>w`4F$O zihqy3|8+o+`w_>?Ib_EA&pVD3JB{H!L58+>Sido}{n&C8cNvBEJ3u(b@ZJv*dsTzZ zj$zM?vM@ZlksQqpR+th|{AA+|a85EFv8>IXp?NeQDzn3B&=E_8SVs&Tn46nB61t_h zyEj(&<|Q+s=um|Pu26}*Po=n;>c-0W_ahofFEg}=3fi}|F-1Lgf^of}4d5Ljne`w*8FSPJu*$^$f_3aBS{7e%_Ni)>aRkbB=IH@DJHe~C5 zV&1HoS_t~H#Raji3DN_K{6|8wc z=fwKB;qSWlKgDjSg7SUla%5u*9B=mL5|kRO57PxW6?!l`V5YMMtr(XyY9`g!w1c_3 z3roN;H}cQr@>K63fE;)}mL~aR=4P%uPsX&pVTm_1?w0c9;kq%8IL{tdg+b7Kh#vz; zDxAk7nY;BQRl?wz3UVRBNr~3GmTCNw#5}C2KFfv3bPHp1fvuR8KLp6er1cpi!((ct4BIif4;V}#sA(eP9XFZoFt{#I;XIX?4pr6-?z7(DR|nKxhdUs9;uH{=Ee}9zoCz3kU*W#+)dM!*1mK%jWf`PfigZlr(=&% zzqtQ5VedQ_mbU9>m4Xz-k_!JFhA`Zo<~@FVtJ2_mAEr$ zS+icmj(sXIw}R!IpY#;<&xS;^^e2?~FaSUYB!keM>$AO|X&|np!nl?U;xZvd>yZ3K zLJWji_cRb|Sr{!}U}RM1n6F6c+lX7j>YvHYWhHBHS8dP+6lzCvDyX6M*=(ju{W5R=A&$Z;WJK@xW1+|H@DG7eHQnsg>fr;X{r8) z!fU68t#NNPNHb-70(g@K+c5_GA59c1s&17&h_TaQb$t!9c!z`MHq3rz^Y>lO`6s3F z+93Q}@(#obCpW3%*7#nq*s>Fn>t|W^p-O#PI~{v8!5IQ@)C7mz)&!Qy%Z>R(7##_i z2~M^VueyFQ>0#*5CO)@hpuZV(o0^YFzYC?btSakOL4m=*u%QLNxkXZGf&PGM)Kfww z@JV#Z*si7r#EJj>#u@65*sg=P^&-c}FYf(0nZD-UAK$us?flF)0#OP>@{MSJC2QWG zEAR>6bii(E65oRfT^L84pjk*?o=EKX_WA67dZtz8DWBm4)F-U-(@>@8Ow@h7b#Jn; zV~7CWWRIX@R=OpTXn(`DU(2*rE7ZyQmAE3m1(9tiU)CrZCuKL*`&enh0pIGVH)_~D zWUj!qyz$&UOJP2aw)f^z^qM|Z4b-Ow{)Kja7)|_iJ4cef^Zk!`sZC^Fv<)f^Q!Ii! z!OGn@FnN}P>r?xXfZ_j#7cMuV{q4PQxt-;2?}ban_}|_Om!i7UT*@u} z_FlLQO8@P>a2a>`5B37{^-1Z!o;6)AME=(e|B16lqI2b}UdYyoK8maSx%Q?;mPRa> z|6ZEB{0{eIb3`PmU&Z|)o zX1;LS8kIXilxWm8f$}!+z3n0XQX;V@ZP=0>sa%MERccFFtHBWfuxCPj2P-!SlOk{800bvT%BM0+5l+TxD9Jut5ymq~$5ox@U1 zusjq~il16)=E~FA6L*#hJ;W%^DfGGtGsl`5 z4+NQn3Us&n!A)KV^deyx+I~YgZCeYKXVv5Qh98zgv8U}{d(@Xc&&k!NtHC*vT58h> z*yNZ-dTx6+f~IuAEvUm3nHy8lwllCo_B#OfSr8SSZk6)wZ?Yuae)ABA=JJw1+>$5t|C9+C?Qy6<)CZ^~Db;T=7$Rugjj)(c9} zglzRWeM|o&ahodG^Jtfl#q<1gz7OK>x4waf+xkJwXOPgf}`(#q0OMt+X4^?Key4hPDJY>{QfzwNAG5QlpL>UtCN?uCD z6)<$M9(>X6l+_X#BXc8+t02CW8QXlVd#Hw-f;oS_fwf~T$>PlQwE!af!HfuEV+k6~ z{s_1SplIpMsf&o5ho)46fZcQYMyl?0)aVBWu3;|ARlj+Se%y%V4=APu`!+&y8@tna z$-70M9blJBbfmv#ktKh-BTx?5q#thu~0rVc}#=&99l4_l5%-+W#=1zZ4;?r$-j-OAoxZv!%D@Yr&-{>j70b1;szZuMSQ5}Q3a{LzC2wS9w8bx!?n)prJao0+V+A2T!%gI@b$ z``+~3W7t=sYTVnbcIEAjJ1%ANTfApb_~olnt70KFlwhlIEdC?ZWK00PH*a`)JH!r_ac|PVm(cZ=-86II|_A7 zoJvY}Rk8FeTTOy^{eq;8Ld}}Mbw^(Mkp`B5BYOT7k z`h$aWPHQbWGO_uJAxTACiI>Lom`y0c1LGPM)1}wgU))LZB`w{A@2trTO*qx}U~Gvl zfYLNxE8dPkEyZPMtX~X%PlKD=Z{*1HYB(e^<}`HyOjb1S@&eehfuEG8#K<}6SkdP= zWnTRyMt17qA|;VHqTZPN(q@a$iG72Udr>==&YXWY@MIwTg?RSrY&wo=(bsPL4SWwE zWL4y_+7i3?gO5hmO#Xeo3X!t$O~NFr97R)?iic^MyQ@V7UhK7oQii(bbgN2TPje&}aMSz8ki-(Vo zkA_J|M1V(xgNKiI0fB*ow1G#sfq-xW4;>X9@Be(9zXM<+1Hxeda4=K=SZo+LY?$*_ zNFrwd3<9LL7ajh`0Rs!^BO(&=4HQ(!FH~RwU}50kVBz5|dJXw)cgW8H@Yo1Alxz(s1b8dfz}HAS5Ctp{1i|xXs9U?>-kd53i`0xP+vX zw2YFnimICWV+{jCBV!X&Gjlt82S<>Tvx|@K3qSvWz@Ui8sOXs3xcG$hjLfX;oZL5g zrDf$6;L57%n&y_)w)T$B_gw>@28V`6M#skI=07hiE`3>ES>4{*-TStGaCmflVb_J7 zKaU@VLD_{3u?rR+9u6Mq!Y&wC5afizhDV@eL&SNYh@@wOOU3Smj3@Fct)%G&H3#r3 zzP@cg3IPr09PRdnX_uD$o?+hqmSuk!c4gN%01Z<70~Q+&8z2NYRZ0(H0Q|?E4Z6dg z=IZ5*&(DMg4M#qQb&$w4PDqOrkn`JDW1b(;Nf!g5YPR@-}~ z#bqRbnO^{QaxHdT*Qfi_P{yufbcC|AmUtkFSiWQFH)9owTan^!Tc14xGzNKRLLR=( z{|L4|jBK*jAWB)(bgh@@zl-lV7QsVAuFzg9Z?XTpxPNKL>KrgfI$ZdjDPx9=54g<< zJF^}$@YXjP%$aTyO3!EL5b}a?v{RJ=BUS(y#E=U6Z=9I)wTDa&HeYYH3t{@bL77~e znMh=*%P3{3oYMMOt+lXD2)N}u?rs6so>CS<(%9;Wb(lQUB8affwv9A4Jy)lli5j1u z9fa@W_NY9bnyrIyGfBP*Fda^jvu?-38S|(C_innX zS1=p2Bt%)~BVlF7gP!q8KKBd}RyNpNaA-4Hoq_)@4@!5+Xu5w4j%k3~rpp4kJKQOe zazLi6eVtPW7RG3w-jN3bC7Kv+k`_M7U}Qh-Od|n*D}({V?=_yHFv7oa65nF@ zS|1i-?_6FWC%4amMHfUo&2mpDh-+M8L06_U8JVtnZOi)%duZ=uO+Z$T>+7^U%_rwF zUfc$Q`~Ag*c=y>tyI^{GHW^Ye7|#K-9p2w$IV>E~MDJBof;meaaNe^Dx#mVZCL&ke zCt9{A#~63A;2FI?r1Vnvw>ULd8aU) z?KcwGkfkQxta_5$4KPk|zDd}(r?Bq;Stlma3(#T?I2$dIgySOyCb@Ft9x3>o)Cy?% zv?8%#>a&;SH({c+$7%0>7 zDNyi5k)W39`}c21_(2F04o1Vmek&NpG_rb{j?9KheXs`JiRDg5(ekC{3Jru-hv{t( z9DPu<;J1UQ$H@5$TukbSTk^P$2k1+e6nQNM2N;`T4_vU6&{>5i&^4^LUM58#mX&*n zS6k^ye8RcU{H543(cz&=PX(AX9-quAQTq5$KO=7XmD?g2c|In7)zU*`mdG5hX!$N2QiknQFs| z=V37UB8<6`&uxnW)ljSX$2Vr1GrdR z>u=xQDmC0ant`6(SDkTJ(WF>yCppYJAo~lP_2#)DSfFtM(E-cqG>>*XdX}wme`2v=nE?Gv}zIu%5cx(%q)c z@o@d^Djf0@uMjNZYq*gL)GPrI!n4!X(bk=cNvFPhL%hn{x_qwe_4J<_DcL(eHL|;S zvd}(AV|cR?3D4CBf=ticnMD<8@R})@K&vo}QIspfrWrV&r;LtvB3OEaWYS~R*9|%0 z&Szn8YVJ*CA|Zyk`WOfg+f3P*mFpIxpgMzRviNNtIorL(Q(NoHHchlf%&@Z)39yt* zs}hKNyu9(vSaH&_8ZA@v(DhR^jn@~l!1+kT!NBAut~_sw^zwH(-HQQcQgB)>ob5Ks z!$Z|?P8sib2$u@|sd3ZofLImCjh6-~x`PcVLh{E{+~Aq5(S>P{Dro;LI|6ZCNQAa* z3o9O^3fQ^md-ue0TrQCbuP!Vb3T~mVcQZ{iS)b6@Or6zRRyrzy<`(DNKYp@%nDD4l zM&hQm4v}^$CnW=Hk38*Oo-j-BWBv!PFy++bNYD|+y(B;F>`;A)vHd*zo*4Bm@>fNJ zfn;0FR0rf3{q-Xfyj^M0WjZaA9CcHf%I28uN07X@*tRe6;Z;lCSu=%f=!zj3wIOAl z+Lq!flls6T@H!>Y-4-l`N!l^FS+n4)OtpR|N&Lh*wNeL7S9?TSV71Tq46m`5Jf&8} zAZkyryn}k7akH&2G(UvTlP3kB6M8DxqyQ??N-v$(RC_6zv zM;O0_bhE#~DBiJEVadN`T?n8uct`AD?cn{y^1cPu&&>Wde+wey%ixY`^S-_ z+;26`isznE?isM71FP*%XQ-v3OonOnQfyqHb{oAq!9Ww--PK@visA8;>xQ(RQ8W=w zS9Jq@$H*5Qe{DfEMh%}`(>=~x=zccvyj(cn%t>ZUxJ;hRcOs=_DM++%PCpN0IqJZ? z(bo$u5>Uc7^Z9^)6f>{yy$%&a1g={`%7!h4{7^y=^G`mlXA3=G!l4bVs z0R7FX^N)hNH}2SO8|OTt|d9By0_^ItCz2aWi( z($*CK0MLUZ54q^X4-5XZ?iMr*y2{cOt`y^!{#;W+T}82=Y0y=Ru4u)O?FxTvRro1h zyR6`JO|}M&gDydEg<~hWhWirFRcd2)R#zAMky}~_)Y|Qw> z*gxsrrEmxu2c4zy3iqAfb#cCK*m zCI1@tV*?K~4_YUG#mkiYUwD^~E;J8XWO~J`RJ_Lf`P5z;39dc6e>z@5>npFw1WMP) zKOd26xkgt?>zfZoa_B-S&_1f=~ zIn{rMe5DBceKPOk-yvVAtbU&?tnoYKE9KJflO;5NhkT_5`YZBZ-Y=)sN=CEM7r3hh9;+YJS7|A5QpneFYlu>#D_7bI5cQ0{Evj4Cv;+ k{-Jrjxvkx`=KuJ!Q$ZF1BGv%_Xdxf+kUa-oju-#?A4!F5O#lD@ literal 0 HcmV?d00001 From 8690adacc3c58dc71cee61973b00414afdbeef4b Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 24 Mar 2024 13:24:31 +0100 Subject: [PATCH 056/134] Remove equals, hashCode and toString methods in IndexedRun The equals, hashCode, and toString methods have been removed from the class IndexedRun. This simplifies the class structure. Future changes to the class properties won't require updating these methods, increasing maintainability. --- .../docxstamper/util/IndexedRun.java | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/util/IndexedRun.java b/src/main/java/org/wickedsource/docxstamper/util/IndexedRun.java index 173a8840..f8c854e1 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/IndexedRun.java +++ b/src/main/java/org/wickedsource/docxstamper/util/IndexedRun.java @@ -74,31 +74,4 @@ private int globalIndexToLocalIndex(int globalIndex) { return globalIndex - startIndex; } } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof IndexedRun that)) return false; - if (endIndex != that.endIndex) return false; - if (indexInParent != that.indexInParent) return false; - return startIndex == that.startIndex; - } - - @Override - public int hashCode() { - int result = startIndex; - result = 31 * result + endIndex; - result = 31 * result + indexInParent; - return result; - } - - @Override - public String toString() { - return String.format( - "[IndexedRun: startIndex=%d; endIndex=%d; indexInParent=%d text=%s}", - startIndex, - endIndex, - indexInParent, - RunUtil.getText(run)); - } } From ec6c60b7472ecbbc38c1456cad080454fc96e1f3 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 31 Mar 2024 05:25:08 +0200 Subject: [PATCH 057/134] Streamline powerpoint stringigication --- .../docxstamper/core/PowerpointParagraph.java | 29 ++-- .../docxstamper/core/PowerpointStamper.java | 2 +- .../docxstamper/core/PowerpointVisitor.java | 114 +++++-------- .../docxstamper/test/BasicPowerpointTest.java | 2 +- .../verron/docxstamper/test/Stringifier.java | 159 +++++++++++------- .../verron/docxstamper/test/Stringifiers.java | 21 --- 6 files changed, 154 insertions(+), 173 deletions(-) delete mode 100644 src/test/java/pro/verron/docxstamper/test/Stringifiers.java diff --git a/src/main/java/pro/verron/docxstamper/core/PowerpointParagraph.java b/src/main/java/pro/verron/docxstamper/core/PowerpointParagraph.java index b75a1b32..4862e003 100644 --- a/src/main/java/pro/verron/docxstamper/core/PowerpointParagraph.java +++ b/src/main/java/pro/verron/docxstamper/core/PowerpointParagraph.java @@ -1,6 +1,8 @@ package pro.verron.docxstamper.core; -import org.docx4j.dml.*; +import org.docx4j.dml.CTRegularTextRun; +import org.docx4j.dml.CTTextCharacterProperties; +import org.docx4j.dml.CTTextParagraph; import org.docx4j.wml.R; import pro.verron.docxstamper.api.Paragraph; import pro.verron.docxstamper.api.Placeholder; @@ -71,20 +73,6 @@ private void addRun(CTRegularTextRun run, int index) { currentPosition = endIndex + 1; } - public String asString(CTTextParagraph paragraph) { - StringBuilder builder = new StringBuilder(); - List egTextRun = paragraph.getEGTextRun(); - for (Object object : egTextRun) { - if (object instanceof CTRegularTextRun run) - builder.append(run.getT()); - else if (object instanceof CTTextField text) - builder.append(text.getT()); - else if (object instanceof CTTextLineBreak) - builder.append("\n"); - } - return builder.toString(); - } - /** * Replaces the given expression with the replacement object within * the paragraph. @@ -188,6 +176,17 @@ else if (expressionWithinRun) { */ @Override public String asString() { + /*StringBuilder builder = new StringBuilder(); + List egTextRun = paragraph.getEGTextRun(); + for (Object object : egTextRun) { + if (object instanceof CTRegularTextRun run) + builder.append(run.getT()); + else if (object instanceof CTTextField text) + builder.append(text.getT()); + else if (object instanceof CTTextLineBreak) + builder.append("\n"); + } + return builder.toString();*/ return runs.stream() .map(PowerpointRun::run) .map(CTRegularTextRun::getT) diff --git a/src/main/java/pro/verron/docxstamper/core/PowerpointStamper.java b/src/main/java/pro/verron/docxstamper/core/PowerpointStamper.java index eb1f63f5..22eefb37 100644 --- a/src/main/java/pro/verron/docxstamper/core/PowerpointStamper.java +++ b/src/main/java/pro/verron/docxstamper/core/PowerpointStamper.java @@ -29,7 +29,7 @@ public void stamp( for (CTTextParagraph paragraph : ctTextParagraphs) { PowerpointParagraph paragraph1 = new PowerpointParagraph( paragraph); - String string = paragraph1.asString(paragraph); + String string = paragraph1.asString(); for (var variable : Placeholders.findVariables(string)) { var replacement = new CTRegularTextRun(); var evaluationContext = new StandardEvaluationContext(context); diff --git a/src/main/java/pro/verron/docxstamper/core/PowerpointVisitor.java b/src/main/java/pro/verron/docxstamper/core/PowerpointVisitor.java index 3d8ce658..de27d63f 100644 --- a/src/main/java/pro/verron/docxstamper/core/PowerpointVisitor.java +++ b/src/main/java/pro/verron/docxstamper/core/PowerpointVisitor.java @@ -3,93 +3,61 @@ import org.docx4j.dml.CTRegularTextRun; import org.docx4j.dml.CTTextBody; import org.docx4j.dml.CTTextParagraph; +import org.docx4j.openpackaging.exceptions.Docx4JException; import org.docx4j.openpackaging.packages.PresentationMLPackage; -import org.docx4j.openpackaging.parts.PresentationML.MainPresentationPart; -import org.docx4j.openpackaging.parts.PresentationML.SlidePart; -import org.pptx4j.Pptx4jException; +import org.docx4j.openpackaging.parts.*; +import org.docx4j.openpackaging.parts.PresentationML.*; +import org.docx4j.openpackaging.parts.WordprocessingML.ImageJpegPart; import org.pptx4j.pml.*; import pro.verron.docxstamper.api.OfficeStamperException; import java.util.List; +import java.util.Map; abstract class PowerpointVisitor { - void visit(Presentation.SldSz element) { - // Do Nothing - } public final void visit(Object object) { before(object); - if (object instanceof PresentationMLPackage element) visit(element); - else if (object instanceof MainPresentationPart element) - visit(element); - else if (object instanceof List elements) visit(elements); - else if (object instanceof SlidePart element) visit(element); - else if (object instanceof Sld element) visit(element); - else if (object instanceof CommonSlideData element) visit(element); - else if (object instanceof GroupShape element) visit(element); - else if (object instanceof Shape element) visit(element); - else if (object instanceof CTTextBody element) visit(element); - else if (object instanceof CTTextParagraph element) visit(element); - else if (object instanceof CTRegularTextRun element) visit(element); - else if (object instanceof Presentation.SldSz element) - visit(element); - else { - System.out.println(object); - throw new UnsupportedOperationException( - "At least one of the methods needs to be subclassed"); - } - } - - protected abstract void before(Object object); - - void visit(List elements) { - for (Object element : elements) { - visit(element); + try { + if (object instanceof PresentationMLPackage element) visit(element.getParts()); + + else if (object instanceof PartName ignored) { /* Do nothing */ } + else if (object instanceof Parts element) visit(element.getParts()); + else if (object instanceof SlideLayoutPart ignored) { /* Do nothing */ } + else if (object instanceof ImageJpegPart ignored) { /* Do nothing */ } + else if (object instanceof ThemePart ignored) { /* Do nothing */ } + else if (object instanceof DocPropsCorePart ignored) { /* Do nothing */ } + else if (object instanceof DocPropsExtendedPart ignored) { /* Do nothing */ } + else if (object instanceof SlideMasterPart ignored) { /* Do nothing */ } + else if (object instanceof ViewPropertiesPart ignored) { /* Do nothing */ } + else if (object instanceof PresentationPropertiesPart ignored) { /* Do nothing */ } + else if (object instanceof TableStylesPart ignored) { /* Do nothing */ } + else if (object instanceof MainPresentationPart element) visit(element.getContents()); + + else if (object instanceof List elements) elements.forEach(this::visit); + else if (object instanceof Map elements) elements.forEach(this::visit); + else if (object instanceof SlidePart element) visit(element.getContents()); + else if (object instanceof Sld element) visit(element.getCSld()); + else if (object instanceof CommonSlideData element) visit(element.getSpTree()); + else if (object instanceof GroupShape element) visit(element.getSpOrGrpSpOrGraphicFrame()); + else if (object instanceof Shape element) visit(element.getTxBody()); + else if (object instanceof CTTextBody element) visit(element.getP()); + else if (object instanceof CTTextParagraph element) visit(element.getEGTextRun()); + else if (object instanceof CTRegularTextRun ignored) { /* Do nothing */ } + else if (object instanceof Presentation.SldSz ignored) { /* Do Nothing */ } + else if (object instanceof Presentation ignored) { /* Do Nothing */ } + else if (object == null) { /* Do Nothing */ } + else throw new OfficeStamperException("Unknown case %s : %s".formatted(object.getClass(), object)); + } catch (Docx4JException e) { + throw new OfficeStamperException(e); } } - void visit(SlidePart element) { - visit(element.getJaxbElement()); + private void visit(Object o1, Object o2) { + visit(o1); + visit(o2); } - void visit(CTTextBody element) { - visit(element.getP()); - } - - void visit(CTRegularTextRun element) { - // Do nothing - } - - void visit(CTTextParagraph element) { - visit(element.getEGTextRun()); - } - - void visit(Shape element) { - visit(element.getTxBody()); - } - - void visit(CommonSlideData element) { - visit(element.getSpTree()); - } - - void visit(GroupShape element) { - visit(element.getSpOrGrpSpOrGraphicFrame()); - } - - void visit(Sld element) { - visit(element.getCSld()); - } - - - void visit(PresentationMLPackage element) { - visit(element.getMainPresentationPart()); - } + protected abstract void before(Object object); - void visit(MainPresentationPart element) { - try { - visit(element.getSlideParts()); - } catch (Pptx4jException e) { - throw new OfficeStamperException(e); - } - } } diff --git a/src/test/java/pro/verron/docxstamper/test/BasicPowerpointTest.java b/src/test/java/pro/verron/docxstamper/test/BasicPowerpointTest.java index 12e45e04..8061a28e 100644 --- a/src/test/java/pro/verron/docxstamper/test/BasicPowerpointTest.java +++ b/src/test/java/pro/verron/docxstamper/test/BasicPowerpointTest.java @@ -30,6 +30,6 @@ record Person(String name) {} Hello Bart """, - Stringifiers.stringifyPowerpoint(presentationMLPackage)); + Stringifier.stringifyPowerpoint(presentationMLPackage)); } } diff --git a/src/test/java/pro/verron/docxstamper/test/Stringifier.java b/src/test/java/pro/verron/docxstamper/test/Stringifier.java index 834592f6..b7b89c05 100644 --- a/src/test/java/pro/verron/docxstamper/test/Stringifier.java +++ b/src/test/java/pro/verron/docxstamper/test/Stringifier.java @@ -3,20 +3,24 @@ import jakarta.xml.bind.JAXBElement; import org.docx4j.TextUtils; import org.docx4j.TraversalUtil; -import org.docx4j.dml.CTBlip; -import org.docx4j.dml.CTBlipFillProperties; -import org.docx4j.dml.Graphic; -import org.docx4j.dml.GraphicData; +import org.docx4j.dml.*; import org.docx4j.dml.picture.Pic; import org.docx4j.dml.wordprocessingDrawing.Inline; import org.docx4j.openpackaging.exceptions.Docx4JException; +import org.docx4j.openpackaging.packages.PresentationMLPackage; +import org.docx4j.openpackaging.packages.SpreadsheetMLPackage; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.openpackaging.parts.PartName; import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage; import org.docx4j.openpackaging.parts.WordprocessingML.CommentsPart; import org.docx4j.wml.*; import org.docx4j.wml.Comments.Comment; +import org.xlsx4j.org.apache.poi.ss.usermodel.DataFormatter; +import org.xlsx4j.sml.Cell; import pro.verron.docxstamper.api.OfficeStamperException; +import pro.verron.docxstamper.core.ExcelCollector; +import pro.verron.docxstamper.core.PowerpointCollector; +import pro.verron.docxstamper.core.PowerpointParagraph; import java.math.BigInteger; import java.security.MessageDigest; @@ -41,6 +45,18 @@ */ public class Stringifier { + public static String stringifyPowerpoint(PresentationMLPackage presentation) { + var collector = new PowerpointCollector<>(CTTextParagraph.class); + collector.visit(presentation); + var collected = collector.collect(); + + var powerpoint = new StringBuilder(); + for (CTTextParagraph paragraph : collected) { + powerpoint.append(new PowerpointParagraph(paragraph).asString()); + } + return powerpoint.toString(); + } + private final Supplier documentSupplier; /** @@ -65,20 +81,23 @@ private static MessageDigest findDigest() { * * @param document the WordprocessingMLPackage document to search for the comment * @param id the ID of the comment to find + * * @return an Optional containing the Comment if found, or an empty Optional if not found + * * @throws Docx4JException if an error occurs while searching for the comment */ public static Optional findComment( WordprocessingMLPackage document, BigInteger id - ) throws Docx4JException { + ) + throws Docx4JException { var name = new PartName("/word/comments.xml"); var parts = document.getParts(); var wordComments = (CommentsPart) parts.get(name); var comments = wordComments.getContents(); return comments.getComment() - .stream() - .filter(idEqual(id)) - .findFirst(); + .stream() + .filter(idEqual(id)) + .findFirst(); } private static Predicate idEqual(BigInteger id) { @@ -125,7 +144,9 @@ private String stringify(R.Tab tab) { *

stringify.

* * @param blip a {@link org.docx4j.dml.CTBlip} object + * * @return a {@link java.lang.String} object + * * @since 1.6.6 */ private String stringify(CTBlip blip) { @@ -135,8 +156,8 @@ private String stringify(CTBlip blip) { .entrySet() .stream() .filter(e -> e.getKey() - .getName() - .contains(blip.getEmbed())) + .getName() + .contains(blip.getEmbed())) .map(Entry::getValue) .findFirst() .map(BinaryPartAbstractImage.class::cast) @@ -153,7 +174,9 @@ private String stringify(CTBlip blip) { *

humanReadableByteCountSI.

* * @param bytes a long + * * @return a {@link java.lang.String} object + * * @since 1.6.6 */ private String humanReadableByteCountSI(long bytes) { @@ -165,7 +188,7 @@ private String humanReadableByteCountSI(long bytes) { ci.next(); } return String.format(Locale.US, "%.1f%cB", bytes / 1000.0, - ci.current()); + ci.current()); } private String sha1b64(byte[] imageBytes) { @@ -179,7 +202,9 @@ private String sha1b64(byte[] imageBytes) { *

stringify.

* * @param o a {@link java.lang.Object} object + * * @return a {@link java.lang.String} object + * * @since 1.6.6 */ public String stringify(Object o) { @@ -213,7 +238,7 @@ private String stringify(CTBlipFillProperties blipFillProperties) { private String stringify(R.CommentReference commentReference) { try { return findComment(document(), - commentReference.getId()) + commentReference.getId()) .map(c -> stringify(c.getContent())) .orElseThrow(); } catch (Docx4JException e) { @@ -243,15 +268,17 @@ private String stringify(Drawing drawing) { private String stringify(List list) { return list.stream() - .map(this::stringify) - .collect(joining()); + .map(this::stringify) + .collect(joining()); } /** *

stringify.

* * @param spacing a {@link org.docx4j.wml.PPrBase.Spacing} object + * * @return a {@link java.util.Optional} object + * * @since 1.6.6 */ private Optional stringify(PPrBase.Spacing spacing) { @@ -266,16 +293,18 @@ private Optional stringify(PPrBase.Spacing spacing) { return map.isEmpty() ? Optional.empty() : Optional.of(map.entrySet() - .stream() - .map(format("%s=%s")) - .collect(joining(",", "{", "}"))); + .stream() + .map(format("%s=%s")) + .collect(joining(",", "{", "}"))); } /** *

stringify.

* * @param p a {@link org.docx4j.wml.P} object + * * @return a {@link java.lang.String} object + * * @since 1.6.6 */ private String stringify(P p) { @@ -290,7 +319,9 @@ private String stringify(P p) { *

extractDocumentRuns.

* * @param p a {@link java.lang.Object} object + * * @return a {@link java.lang.String} object + * * @since 1.6.6 */ public String extractDocumentRuns(Object p) { @@ -299,7 +330,7 @@ public String extractDocumentRuns(Object p) { return runCollector .runs() .filter(r -> !r.getContent() - .isEmpty()) + .isEmpty()) .map(this::stringify) .collect(joining()); } @@ -309,23 +340,23 @@ private Optional stringify(PPr pPr) { return Optional.empty(); var set = new TreeSet(); if (pPr.getJc() != null) set.add("jc=" + pPr.getJc() - .getVal() - .value()); + .getVal() + .value()); if (pPr.getInd() != null) set.add("ind=" + pPr.getInd() - .getLeft() - .intValue()); + .getLeft() + .intValue()); if (pPr.getKeepLines() != null) set.add("keepLines=" + pPr.getKeepLines() - .isVal()); + .isVal()); if (pPr.getKeepNext() != null) set.add("keepNext=" + pPr.getKeepNext() - .isVal()); + .isVal()); if (pPr.getOutlineLvl() != null) set.add("outlineLvl=" + pPr.getOutlineLvl() - .getVal() - .intValue()); + .getVal() + .intValue()); if (pPr.getPageBreakBefore() != null) set.add("pageBreakBefore=" + pPr.getPageBreakBefore() - .isVal()); + .isVal()); if (pPr.getPBdr() != null) set.add("pBdr=xxx"); if (pPr.getPPrChange() != null) set.add("pPrChange=xxx"); stringify(pPr.getRPr()) @@ -358,7 +389,9 @@ private Optional stringify(PPr pPr) { *

stringify.

* * @param run a {@link org.docx4j.wml.R} object + * * @return a {@link java.lang.String} object + * * @since 1.6.6 */ private String stringify(R run) { @@ -375,7 +408,9 @@ private String stringify(R run) { *

stringify.

* * @param rPr a {@link org.docx4j.wml.RPrAbstract} object + * * @return a {@link java.lang.String} object + * * @since 1.6.6 */ private Optional stringify(RPrAbstract rPr) { @@ -383,70 +418,70 @@ private Optional stringify(RPrAbstract rPr) { return Optional.empty(); var set = new TreeSet(); if (rPr.getB() != null) set.add("b=" + rPr.getB() - .isVal()); + .isVal()); if (rPr.getBdr() != null) set.add("bdr=xxx"); if (rPr.getCaps() != null) set.add("caps=" + rPr.getCaps() - .isVal()); + .isVal()); if (rPr.getColor() != null) set.add("color=" + rPr.getColor() - .getVal()); + .getVal()); if (rPr.getDstrike() != null) set.add("dstrike=" + rPr.getDstrike() - .isVal()); + .isVal()); if (rPr.getI() != null) set.add("i=" + rPr.getI() - .isVal()); + .isVal()); if (rPr.getKern() != null) set.add("kern=" + rPr.getKern() - .getVal() - .intValue()); + .getVal() + .intValue()); if (rPr.getLang() != null) set.add("lang=" + rPr.getLang() - .getVal()); + .getVal()); //if (rPr.getRFonts() != null) set.add("rFonts=xxx:" + rPr.getRFonts().getHint().value()); if (rPr.getRPrChange() != null) set.add("rPrChange=xxx"); if (rPr.getRStyle() != null) set.add("rStyle=" + rPr.getRStyle() - .getVal()); + .getVal()); if (rPr.getRtl() != null) set.add("rtl=" + rPr.getRtl() - .isVal()); + .isVal()); if (rPr.getShadow() != null) set.add("shadow=" + rPr.getShadow() - .isVal()); + .isVal()); if (rPr.getShd() != null) set.add("shd=" + rPr.getShd() - .getColor()); + .getColor()); if (rPr.getSmallCaps() != null) set.add("smallCaps=" + rPr.getSmallCaps() - .isVal()); + .isVal()); if (rPr.getVertAlign() != null) set.add("vertAlign=" + rPr.getVertAlign() - .getVal() - .value()); + .getVal() + .value()); if (rPr.getSpacing() != null) set.add("spacing=" + rPr.getSpacing() - .getVal() - .intValue()); + .getVal() + .intValue()); if (rPr.getStrike() != null) set.add("strike=" + rPr.getStrike() - .isVal()); + .isVal()); if (rPr.getOutline() != null) set.add("outline=" + rPr.getOutline() - .isVal()); + .isVal()); if (rPr.getEmboss() != null) set.add("emboss=" + rPr.getEmboss() - .isVal()); + .isVal()); if (rPr.getImprint() != null) set.add("imprint=" + rPr.getImprint() - .isVal()); + .isVal()); if (rPr.getNoProof() != null) set.add("noProof=" + rPr.getNoProof() - .isVal()); + .isVal()); if (rPr.getSpecVanish() != null) set.add("specVanish=" + rPr.getSpecVanish() - .isVal()); + .isVal()); if (rPr.getU() != null) set.add("u=" + rPr.getU() - .getVal() - .value()); + .getVal() + .value()); if (rPr.getVanish() != null) set.add("vanish=" + rPr.getVanish() - .isVal()); + .isVal()); if (rPr.getW() != null) set.add("w=" + rPr.getW() - .getVal()); + .getVal()); if (rPr.getWebHidden() != null) set.add("webHidden=" + rPr.getWebHidden() - .isVal()); + .isVal()); if (rPr.getHighlight() != null) set.add("highlight=" + rPr.getHighlight() - .getVal()); + .getVal()); if (rPr.getEffect() != null) set.add("effect=" + rPr.getEffect() - .getVal() - .value()); + .getVal() + .value()); if (set.isEmpty()) return Optional.empty(); return Optional.of(String.join(",", set)); @@ -478,13 +513,13 @@ private Optional stringify(SectPr sectPr) { private String stringify(SectPr.PgSz pgSz) { var set = new TreeSet(); if (pgSz.getOrient() != null) set.add("orient=" + pgSz.getOrient() - .value()); + .value()); if (pgSz.getW() != null) set.add("w=" + pgSz.getW() - .intValue()); + .intValue()); if (pgSz.getH() != null) set.add("h=" + pgSz.getH() - .intValue()); + .intValue()); if (pgSz.getCode() != null) set.add("code=" + pgSz.getCode() - .intValue()); + .intValue()); return String.join(",", set); } } diff --git a/src/test/java/pro/verron/docxstamper/test/Stringifiers.java b/src/test/java/pro/verron/docxstamper/test/Stringifiers.java deleted file mode 100644 index 8b334f35..00000000 --- a/src/test/java/pro/verron/docxstamper/test/Stringifiers.java +++ /dev/null @@ -1,21 +0,0 @@ -package pro.verron.docxstamper.test; - -import org.docx4j.dml.CTTextParagraph; -import org.docx4j.openpackaging.packages.PresentationMLPackage; -import pro.verron.docxstamper.core.PowerpointCollector; -import pro.verron.docxstamper.core.PowerpointParagraph; - -public class Stringifiers { - public static String stringifyPowerpoint(PresentationMLPackage presentation) { - var collector = new PowerpointCollector<>(CTTextParagraph.class); - collector.visit(presentation); - var collected = collector.collect(); - - var powerpoint = new StringBuilder(); - for (CTTextParagraph paragraph : collected) { - powerpoint.append(new PowerpointParagraph(paragraph).asString()); - } - return powerpoint.toString(); - } - -} From ff85fc89c2c79b4a0845d0c1560e15d7a5742511 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 31 Mar 2024 05:29:22 +0200 Subject: [PATCH 058/134] Add a basic case of excel stamping --- .../docxstamper/core/ExcelCollector.java | 33 +++++++++ .../docxstamper/core/ExcelParagraph.java | 54 +++++++++++++++ .../verron/docxstamper/core/ExcelStamper.java | 46 +++++++++++++ .../verron/docxstamper/core/ExcelVisitor.java | 63 ++++++++++++++++++ .../docxstamper/preset/OfficeStampers.java | 6 ++ .../docxstamper/test/BasicExcelTest.java | 46 +++++++++++++ .../verron/docxstamper/test/Stringifier.java | 11 +++ test/sources/excel-base.xlsx | Bin 0 -> 8426 bytes 8 files changed, 259 insertions(+) create mode 100644 src/main/java/pro/verron/docxstamper/core/ExcelCollector.java create mode 100644 src/main/java/pro/verron/docxstamper/core/ExcelParagraph.java create mode 100644 src/main/java/pro/verron/docxstamper/core/ExcelStamper.java create mode 100644 src/main/java/pro/verron/docxstamper/core/ExcelVisitor.java create mode 100644 src/test/java/pro/verron/docxstamper/test/BasicExcelTest.java create mode 100644 test/sources/excel-base.xlsx diff --git a/src/main/java/pro/verron/docxstamper/core/ExcelCollector.java b/src/main/java/pro/verron/docxstamper/core/ExcelCollector.java new file mode 100644 index 00000000..fa96d5ca --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/core/ExcelCollector.java @@ -0,0 +1,33 @@ +package pro.verron.docxstamper.core; + +import java.util.ArrayList; +import java.util.List; + +public class ExcelCollector + extends ExcelVisitor { + private final Class aClass; + private final List list = new ArrayList<>(); + + public ExcelCollector(Class aClass) { + this.aClass = aClass; + } + + public static List collect( + Object template, + Class aClass + ) { + var collector = new ExcelCollector<>(aClass); + collector.visit(template); + return collector.collect(); + } + + public List collect() { + return list; + } + + @Override + protected void before(Object object) { + if (aClass.isInstance(object)) + list.add(aClass.cast(object)); + } +} diff --git a/src/main/java/pro/verron/docxstamper/core/ExcelParagraph.java b/src/main/java/pro/verron/docxstamper/core/ExcelParagraph.java new file mode 100644 index 00000000..2f9b9d99 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/core/ExcelParagraph.java @@ -0,0 +1,54 @@ +package pro.verron.docxstamper.core; + +import org.xlsx4j.sml.CTRst; +import pro.verron.docxstamper.api.Placeholder; + +public class ExcelParagraph { + private final CTRst paragraph; + + /** + * Constructs a new ParagraphWrapper for the given paragraph. + * + * @param paragraph the paragraph to wrap. + */ + public ExcelParagraph(CTRst paragraph) { + this.paragraph = paragraph; + } + + /** + * Replaces the given expression with the replacement object within + * the paragraph. + * The replacement object must be a valid DOCX4J Object. + * + * @param placeholder the expression to be replaced. + * @param replacement the object to replace the expression. + */ + public void replace(Placeholder placeholder, String replacement) { + var ctXstringWhitespace = paragraph.getT(); + var string = ctXstringWhitespace.getValue(); + var start = string.indexOf(placeholder.expression()); + var end = start + placeholder.expression() + .length(); + var next = string.substring(0, start) + replacement + string.substring(end); + ctXstringWhitespace.setValue(next); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return asString(); + } + + /** + * Returns the aggregated text over all runs. + * + * @return the text of all runs. + */ + + public String asString() { + return paragraph.getR() + ": " + paragraph.getT() + .getValue(); + } +} diff --git a/src/main/java/pro/verron/docxstamper/core/ExcelStamper.java b/src/main/java/pro/verron/docxstamper/core/ExcelStamper.java new file mode 100644 index 00000000..bc80e168 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/core/ExcelStamper.java @@ -0,0 +1,46 @@ +package pro.verron.docxstamper.core; + +import org.docx4j.openpackaging.exceptions.Docx4JException; +import org.docx4j.openpackaging.packages.SpreadsheetMLPackage; +import org.springframework.expression.spel.SpelParserConfiguration; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.xlsx4j.sml.CTRst; +import pro.verron.docxstamper.api.OfficeStamper; +import pro.verron.docxstamper.api.OfficeStamperException; + +import java.io.OutputStream; + +import static pro.verron.docxstamper.core.Placeholders.findVariables; + +public class ExcelStamper + implements OfficeStamper { + + @Override + public void stamp( + SpreadsheetMLPackage template, + Object context, + OutputStream outputStream + ) + throws OfficeStamperException { + var paragraphs = ExcelCollector.collect(template, CTRst.class); + for (CTRst cell : paragraphs) { + var paragraph = new ExcelParagraph(cell); + var string = paragraph.asString(); + for (var variable : findVariables(string)) { + var evaluationContext = new StandardEvaluationContext(context); + var parserConfiguration = new SpelParserConfiguration(); + var parser = new SpelExpressionParser(parserConfiguration); + var expression = parser.parseExpression(variable.content()); + var value = expression.getValue(evaluationContext); + var stringValue = String.valueOf(value); + paragraph.replace(variable, stringValue); + } + } + try { + template.save(outputStream); + } catch (Docx4JException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/pro/verron/docxstamper/core/ExcelVisitor.java b/src/main/java/pro/verron/docxstamper/core/ExcelVisitor.java new file mode 100644 index 00000000..27a9e334 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/core/ExcelVisitor.java @@ -0,0 +1,63 @@ +package pro.verron.docxstamper.core; + +import jakarta.xml.bind.JAXBElement; +import org.docx4j.openpackaging.exceptions.Docx4JException; +import org.docx4j.openpackaging.packages.SpreadsheetMLPackage; +import org.docx4j.openpackaging.parts.DocPropsCorePart; +import org.docx4j.openpackaging.parts.DocPropsExtendedPart; +import org.docx4j.openpackaging.parts.Parts; +import org.docx4j.openpackaging.parts.SpreadsheetML.SharedStrings; +import org.docx4j.openpackaging.parts.SpreadsheetML.Styles; +import org.docx4j.openpackaging.parts.SpreadsheetML.WorkbookPart; +import org.docx4j.openpackaging.parts.SpreadsheetML.WorksheetPart; +import org.docx4j.openpackaging.parts.ThemePart; +import org.xlsx4j.sml.*; +import pro.verron.docxstamper.api.OfficeStamperException; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +abstract class ExcelVisitor { + + public final void visit(Object object) { + before(object); + try { + if (object instanceof SpreadsheetMLPackage element) visit(element.getParts()); + + else if (object instanceof Parts element) visit(element.getParts()); + else if (object instanceof WorksheetPart element) visit(element.getContents()); + else if (object instanceof WorkbookPart element) visit(element.getContents()); + else if (object instanceof DocPropsCorePart ignored) { /* do nothing */ } + else if (object instanceof DocPropsExtendedPart ignored) { /* do nothing */ } + else if (object instanceof Styles ignored) { /* do nothing */ } + else if (object instanceof SharedStrings element) {visit(element.getContents());} + else if (object instanceof ThemePart ignored) { /* do nothing */ } + + else if (object instanceof Workbook element) visit(element.getSheets()); + else if (object instanceof Sheets element) visit(element.getSheet()); + else if (object instanceof Worksheet element) visit(element.getSheetData()); + else if (object instanceof SheetData element) visit(element.getRow()); + else if (object instanceof Row element) visit(element.getC()); + else if (object instanceof Cell element) visit(element.getIs()); + else if (object instanceof CTRst element) visit(element.getR()); + else if (object instanceof CTSst element) visit(element.getSi()); + else if (object instanceof CTRElt element) visit(element.getT()); + else if (object instanceof CTXstringWhitespace ignored) { /* do nothing */ } + else if (object instanceof JAXBElement element) visit(element.getValue()); + else if (object instanceof Sheet element) visit(element.getState()); + else if (object instanceof STSheetState ignored) { /* do nothing */ } + + else if (object instanceof List element) element.forEach(this::visit); + else if (object instanceof Set element) element.forEach(this::visit); + else if (object instanceof Map element) visit(element.entrySet()); + else if (object instanceof Map.Entry element) visit(element.getValue()); + else if (object == null) { /* do nothing */ } + else throw new OfficeStamperException("Unknown case : %s %s".formatted(object, object.getClass())); + } catch (Docx4JException e) { + throw new OfficeStamperException(e); + } + } + + protected abstract void before(Object object); +} diff --git a/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java b/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java index 6a8f226b..cc45e359 100644 --- a/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java +++ b/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java @@ -1,12 +1,14 @@ package pro.verron.docxstamper.preset; import org.docx4j.openpackaging.packages.PresentationMLPackage; +import org.docx4j.openpackaging.packages.SpreadsheetMLPackage; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.wickedsource.docxstamper.DocxStamper; import org.wickedsource.docxstamper.preprocessor.MergeSameStyleRuns; import org.wickedsource.docxstamper.preprocessor.RemoveProofErrors; import pro.verron.docxstamper.api.OfficeStamper; import pro.verron.docxstamper.api.OfficeStamperConfiguration; +import pro.verron.docxstamper.core.ExcelStamper; import pro.verron.docxstamper.core.PowerpointStamper; /** @@ -40,4 +42,8 @@ public static OfficeStamper docxStamper() { public static OfficeStamper pptxStamper() { return new PowerpointStamper(); } + + public static OfficeStamper xlsxStamper() { + return new ExcelStamper(); + } } diff --git a/src/test/java/pro/verron/docxstamper/test/BasicExcelTest.java b/src/test/java/pro/verron/docxstamper/test/BasicExcelTest.java new file mode 100644 index 00000000..f69b5c28 --- /dev/null +++ b/src/test/java/pro/verron/docxstamper/test/BasicExcelTest.java @@ -0,0 +1,46 @@ +package pro.verron.docxstamper.test; + +import org.docx4j.openpackaging.exceptions.Docx4JException; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.docx4j.openpackaging.packages.SpreadsheetMLPackage.load; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static pro.verron.docxstamper.preset.OfficeStampers.xlsxStamper; +import static pro.verron.docxstamper.test.IOStreams.getInputStream; +import static pro.verron.docxstamper.test.IOStreams.getOutputStream; +import static pro.verron.docxstamper.test.Stringifier.stringifyExcel; + +public class BasicExcelTest { + @Test + public void testStamper() + throws IOException, Docx4JException { + + var stamper = xlsxStamper(); + var templatePath = Path.of("test", "sources", "excel-base.xlsx"); + var templateStream = Files.newInputStream(templatePath); + + var templatePackage = load(templateStream); + var templateExpectedString = """ + A1: Hello + B1: ${name}"""; + var templateActualString = stringifyExcel(templatePackage); + assertEquals(templateExpectedString, templateActualString); + var stampedOutputStream = getOutputStream(); + record Person(String name) {} + var context = new Person("Bart"); + + stamper.stamp(templatePackage, context, stampedOutputStream); + + var stampedReadStream = getInputStream(stampedOutputStream); + var stampedPackage = load(stampedReadStream); + var stampedExpectedString = """ + A1: Hello + B1: Bart"""; + var stampedActualString = stringifyExcel(stampedPackage); + assertEquals(stampedExpectedString, stampedActualString); + } +} diff --git a/src/test/java/pro/verron/docxstamper/test/Stringifier.java b/src/test/java/pro/verron/docxstamper/test/Stringifier.java index b7b89c05..cb718b1c 100644 --- a/src/test/java/pro/verron/docxstamper/test/Stringifier.java +++ b/src/test/java/pro/verron/docxstamper/test/Stringifier.java @@ -68,6 +68,17 @@ public Stringifier(Supplier documentSupplier) { this.documentSupplier = documentSupplier; } + + public static String stringifyExcel(SpreadsheetMLPackage presentation) { + var collector = new ExcelCollector<>(Cell.class); + collector.visit(presentation); + var formatter = new DataFormatter(); + return collector.collect() + .stream() + .map(cell -> cell.getR() + ": " + formatter.formatCellValue(cell)) + .collect(joining("\n")); + } + private static MessageDigest findDigest() { try { return MessageDigest.getInstance("SHA-1"); diff --git a/test/sources/excel-base.xlsx b/test/sources/excel-base.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..671283bf762971276ebbf36d029efd8da791b78c GIT binary patch literal 8426 zcmeHMgUZ?b`wY9&w~ z9)$GcF{_Kc?Mdw`H#f+)wY|%az6MG?C$!_Q!5drh<^DWkQ|su^5E9kbs(~3BJn&Y_ zn4F)Zw|}3mgP1hMRbTI@kbqo*i`2v<-YN%>>C)J(!7H;S392NsAtVwD+Q?hz*T!Ax zYsM_sFBKeE>|9mR{(vhmR5k_Nn82U5vt{x%KG|SXK#ZmKT_W-@exXLCtI1@h(d-J% zP;7T&Y~q=DVh;a-Ga{)CdwFyPxwW1DzA`UaZCT7|xqe@gJxhn5p`T9&V?yky8nlIv z(8h$#AYUSwDr9mVpk^OiM^we_IOg9YPFSooeB4F3d`-88PtmhNZqbsTm}H?sLhZo1 z`l3HyMpclXIV9lKz+jWWTh#+|3O~n}6DXUAvX^%R=eRp9CvT;IgKw#5{UZ-n2X=&g z_s2gPgdTo=wiC#GiwpqV-5~(f{zl7sEl!$K7}k_vtiyoO(!|vg?8d?VZTydp|6&gQ z<<=`xlvTSqF(Z!TzeWyR%q%AXr4&6SWDFi-#=lZ7HY3TzAb zI<&Yf62Ch@b-vD98H-0COw-_56_EjTd5XeJ@A^#6rE;wY*JI{v<~-{W$eYolEs?pR zsr0$x;0lf0)MuF*;266O85;fziU=antYG~*)J#6Bqf08^-6kVM2V;|xZSDHw%tU{A&i z%;D+eYH#M`WdBXKDz%K9aySY63+nEIzN&O4B_XCizADr&afoRNctfd`;1WXo_8p&c zdHJ3HTnVl6$z;8BbiDtX^I_YTae)aqcFE_7hKsr!YC-dW866=LhYoH_b9>$OfJSy2 z)1d4i_2~dX3rWC@YdfxM8_>)FPNIxD&DJP9q1dJmy=YXX7X)DqXN`(@VJKLh5?8`( zu0-6t(d@WN5SC#R2^_k367?}flaH{DUAN4WGUcNTf`9sFP`wS3KaQc~W=l1gIR>4Q zNMs9I11GC3+5NtpAcfV{**CUzR=bNMNZiBpc!48$XHygDl?G$Yv^C&hU%ucn(FkRu z7zkE1KNc%Vpm~qLFSztNr4vOD$+vP95V5RUY?`E9oDK}RkgPk*&xwrKQp|t7nCH%> zp(UG-Co*-h%hx8nHZnWP9Y0qY@3=()Fob;s)kR20$)qh)*47ovC7`99qh)& z)>NR}Ax#vJj52^`aleU=PB^sC{I0!(yXQhxQY8A3xkzpQlndBXZX;jxIJE2lId%Tv zs`%D@T|AVB8?}tGe$;r2RAOV$zZuNaL8`y_+GU=%!C;hc zmlE@JU4!QvrCxfea{zRGW5RAK^U$lDXPod5t7Tx}z~9_29nNGQ%Ljt?h5rq;dk;!3 zUkUq~T?CETD2tRb0+QJXI6Z!wgLHsY`W4?uJsMY(2=7YL>$-`?sdhO9m90r`&66jq zSP&;aD}x)O9V_I|E`^c8hoxKCLJj2CMy*p1ygWKKe9udG&ek0{^7}Tb2NMJVU3U}6 zIb^7dslAYQQPXoN;}3Hm>3{|4(KqYdP%lHs3}#iXXOB5x`2QzEg2mD@PGH}(z*s^C zK!t-b51VR_x&fwqe{xa(d=?5aPaf=b+x#+QUH|tYccF zr{W1XT_I;~G18q?uGK;Y9QBOPgE@k?r%)8@$tUb|7wr`&dKgDw*cE>SRW}<;OLsSp?-SRz=$f0V=v2%J>_19}-eX+}4aS=; zOe$B_a&W3REVed}c+!hA>tU>a)_NqtSE9kLj+<%PcY9;KZ+Z)vivf3f+bS!hGQLEN zgVgg@q})QUxSL<~pg7u9R|cote>)Y~dobc>y}Zx}2&U}3OlhEUM>03AD{M{#qaf47 zKg~w@>dx{>80F13QN4bxp{x znhl&)mN((wbv8)QZ35VZPA^4Z2&ru%2HgHoZdKYaAvd*ULC{`g)hWD!dbU zDUxgUz@6(ix_oC7LYMKhQ!b`_ydk2K=Yau=^H-{TXm6l!=rVw5dB&Z^*R>wYs(!LM zaWdjc+tG%I&Ea&6T9cL$_inAnCGe5vOOM}+m9=4F-7F`^`0DDPvG0u+(cTI?A+oG2sbcHKAx~pbRV>vrBkR5K6b!AD?vjP+NjqSzv+ZkKL zmc-U2FQ3hS3q}8gr?PkuYZy%LgUG(^SHHv4-Nw?vlH>dM9hv)jV@`{F1noHUca+IqLX|MHP2XgN-~%ISa`>NFj^Z-9hk+wi!44Yz>Er z^E7k@7E1g#$?(hLB&f8zzKIW3e4&@pb50^ErEU>CD!L0U_H!b0ojYO53PMLs7mvC@ z!%v$eb_mp;ttYg0Ew$C$U0KgfxCdL zS)J_iN^h<5Kc8vY3ZFqto_?y47PKh*nl$T)HLX$0wfz9D*ocf-G_Z0@M<;jFTGAqSL&oePRVdP?*X{l1?rb@8`3t~il-A;mB$s5Xup1Nn;1 zZ1|H%izBtYgpVUfeAeE`NInFYD|&F5m(6S0qxtmIQUPd_s3kQXRT6nc(5v<07Fp!t zZbiK_XMBSMB8{~A?GURC`D=!_iBVZpp=cbh{UAo>aTWbw|5|Dst4x0znz0eq7v~)c zPTW+5i3(RUU5X<2YJ)e>tHnTAetUJ@SYRxA&3<%BoxgXP(GhU-B~jdD>wcd;@>=i3 zn91$!*It~JuV=$Nu@{aJeL{XRc<=HSFPED8@kp}6#qV1?S#!nUan@KsFgzp&)NttebQwHlKFzl)CdQL|tWRl2CEhiSx&fz;o;GiopeA(| z^neO?Q9i%2Hadaa87?c|d?>e*z|HY>b84qoeaQ;EL(Qw3%xXOREHqcU6YnsRsbqf- zvR+0#UBOh|*&35$O-A+2CCe^kajQ?{)JIe}jLTVvD2U`c?S`m_p_RL7L-5I}&$8$; zP%^WQHeyDB+RO;$PEp=clZxp#AumxL=uaK&`XSjldVUoX`Y;l8<{+}Q?4ylB?+=7tt-KOAzqZ?E5aK`*0k&pd= zz5St0L)(|0NU6JofW644NNHJ)iq*oA439E07RW6YD4(b2C`hq%lPJ5W=Rr*ewLY~N zy__y&kgf?J2a%1;Mh?^yBx5W{Xc{ru?B;qdL2%!C&m*!49b%qCowHaAJ(>)6sf8jD zUPljnA-*+AvrKlGcqB54P%Xot6B>q^E0{)mPX{|t6N^`itf*;bmlE{>TQlFlP`?~@ ziX%Pgn3WgxX`MYA_v95DqhB@Q?5m-#%bk-cY*fn(pHG)arz|~iB%_f=vN2cFoCUs= zeVoX}6X;$-)zb?krhh5raV9OJCG9O-6iY(L6T|*V&@g&CBB5omN1~Ob4-;+~d(DfT zVg4fjDMvxMz0zV)@G$G@e~gKpnc_#!t|4L#l{7`V~xE9`>If>`cb@$cP&nw zR5sH?qnLInpBpe0N7(h1?R1(b$Ek4-qZX|aAad(Q)&Y=K-Fnv$-QQTW)+!S3K`&7G z0ua|HB2!oB2n}E0wewk^68T@fcVQ<#ju9d1CBv=GwUiS&#OK(Cs*6T38?%r+{u*(l zr7zj2zJn}T%!g`c+@TYQjvpJ9s-F~SL)wK)U_@A8@c4R`A;ZFd$ity4g;}%Uad}J= zbW@|^=yr1*2+U(J^`CdF^EG|3yp7Eg&@oqDR}9hFiBx|rzW+$FLey_p$Qpg$lRWtW zwthNTEiu$G(LB>AUk4Zo9GrBJG1F5 z^OO(Q)QOufCbZ`c(Ve5K?Tu?Q+_x-LY`o>*H|MBi(a<(x7lcnhC1_mW{x&}WRr{)3 z*%yxK2wcIXPMzy|J4fZm1#jGZGOLLjdK)+iy5IW{OW*a(im4U`g~#NO`Q{ETZjVr6KGSQ zj1%VaBNuKV322yi%k0aLL9vUC?IF!hI}{nL74XfjJ3@#30Yz!SE5cXgRi(!-Few=j zhM2N|i2=}#8RB`P_{S_5JBJmR-J)NV1+vu!Iw_7L;`<*IW6e87$Qt=a<3!Oc5SOU# zB+m(V7u-B0XLBis@r=-1b$!>f2ZRqOYyGjBWtGNYuZePVXY)3 z$BYUsE}V_y^SNy}ukN#>l?svY*m?;3eKa@5bG7x2_Ze#wR)s5LjN|v+3i_vx&GEt| zUGB+sT7axO!9ID2vUt4}I~Lv<1cOn&UxuuyY1Mf}1oHiAoejA=XVfc-rOY{^5EnZL zBojbMCk2wX_F5|5WfQJ#&|>tEW&LY;PfV|F7DB9(}=Wzo>f1|14x!>3$2 zmV(SaPeU`&*hG?^dz@sM5cuA}tZK8cq0KROzpgoxy`6YpgkRm+Ks={_ z3M}!ohmjQ*wqLezGFNkTa(3e|cXGA-qZjhuHW{|$ekmr3-JAryD@YyEgT6U+%YB#* zD9pYtVqRi&%dnq=F~lGK+-us6qg(PEpz- zg=kgA8U@lU7nAM_+RbeM5nIe~Y@Gd2dN4{cFNsbFmWYD>(9;BuQQMfBkHV!{@o%Jk zT#A)Oa>cXRX5%_uv#o|Dkydwdby9S*aZV%P-b)U_k-sr5$EFmBK(|X8T^ES?+&C9p zc2Izj$L%`%LfB?yhs5(8(Hmlvix1C2YizSi6|{E)tDqAZZ8H0)^E0xvmKB zm^7D|FnowbLb*UzUL|z~m6=ajI*ygm)}hCXevZeR)$)lbxR*9{9IhJ1lR1lLc!X%t zuc)%ZEm{(`c-xD1Hmm&a`u)i3J4zFoZt3V#lS)jbX2aCDUrkxah`_~Q7WgNw|57b{ zF8y<3VIU-hK@|8`5SltW|A#>s`2IL@Q#u_NzZJld&aop7<8vJuVyz5jXMZ^I0YGVegbk{ zil#c(GT8`~p3siQpl$gqG6nm4UH(;W<|{ol4%C}T?I)VE<;Aj*5mL;xH@)_)xV_{S z`!38AlC`VRHP8kwQZr&6x|=wZJ&L3Hz7Ujb1RB>3M(nlIcf1WNr+zl8tF<-!B*D5( zGRwN_rs6q-v`~~QKadz_+!Fq*%cnG_CLvX5w=d}e>TOS#Ak^{3_d}nu7h+dpy)n^@ z4-T4rK=jGN7}8l6xpFnX-eKnvHvhClTLewe<2C0{>v2!1yH zd%5(7DFE;R?Yr^+Q#}3I&(AfqAD(bA|Mw^UQBnKZ%g?2bA6_8Xzbt(G?BM6L^M?aV z!tV}#B%wc>{w(o7OnG5NLD*w-`&saR_V9Np{b3IPV3Ps>{}9%n&Ho+*|7xBJ%Srxc a{zphuQ$&KTE&zZIdj-L?`| Date: Sun, 31 Mar 2024 05:30:09 +0200 Subject: [PATCH 059/134] Publish code styles setting --- intellij-style.xml | 158 ++++++++++++++++++++++----------------------- 1 file changed, 79 insertions(+), 79 deletions(-) diff --git a/intellij-style.xml b/intellij-style.xml index b37f0d59..8a82d342 100644 --- a/intellij-style.xml +++ b/intellij-style.xml @@ -1,80 +1,80 @@ - - - - - + + + + + From 743a0b76a630a79d419d08472f7b10dc9ee8e5a3 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 31 Mar 2024 05:31:01 +0200 Subject: [PATCH 060/134] reorder opens and exports in module-info --- src/main/java/module-info.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index d9c3fe97..8a2cad50 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -11,12 +11,11 @@ opens pro.verron.docxstamper.api; opens pro.verron.docxstamper.preset; - opens pro.verron.docxstamper.core to pro.verron.officestamper.test; - exports pro.verron.docxstamper to pro.verron.officestamper.test; - exports pro.verron.docxstamper.api; exports pro.verron.docxstamper.preset; + opens pro.verron.docxstamper.core to pro.verron.officestamper.test; + exports pro.verron.docxstamper.core to pro.verron.officestamper.test; /** * TODO: remove all the following exports in next version */ From 72e9013dc22980ca1f4edaafdfd03628764b1059 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 31 Mar 2024 05:42:18 +0200 Subject: [PATCH 061/134] Convert all markdown to asciidoctor --- CODE_OF_CONDUCT.md => CODE_OF_CONDUCT.adoc | 66 ++++---- CONTRIBUTING.adoc | 169 +++++++++++++++++++++ CONTRIBUTING.md | 164 -------------------- SECURITY.md => SECURITY.adoc | 20 +-- 4 files changed, 209 insertions(+), 210 deletions(-) rename CODE_OF_CONDUCT.md => CODE_OF_CONDUCT.adoc (68%) create mode 100644 CONTRIBUTING.adoc delete mode 100644 CONTRIBUTING.md rename SECURITY.md => SECURITY.adoc (64%) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.adoc similarity index 68% rename from CODE_OF_CONDUCT.md rename to CODE_OF_CONDUCT.adoc index a06f425e..20f2d2f2 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.adoc @@ -1,6 +1,6 @@ -# Code of Conduct - Docx Stamper += Code of Conduct - Docx Stamper -## Our Pledge +== Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and @@ -9,7 +9,7 @@ size, disability, ethnicity, sex characteristics, gender identity and expression level of experience, education, socioeconomic status, nationality, personal appearance, race, religion, or sexual identity and orientation. -## Our Standards +== Our Standards Examples of behavior that contributes to a positive environment for our community include: @@ -17,23 +17,18 @@ community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the - overall community +* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexual language or imagery, and sexual attention or - advances +* The use of sexual language or imagery, and sexual attention or advances * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment -* Publishing others' private information, such as a physical or email - address, without their explicit permission -* Another conduct which could reasonably be considered inappropriate in a - professional setting +* Publishing others' private information, such as a physical or email address, without their explicit permission +* Another conduct which could reasonably be considered inappropriate in a professional setting -## Our Responsibilities +== Our Responsibilities Project maintainers are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in @@ -45,7 +40,7 @@ comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. -## Scope +== Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. @@ -53,64 +48,63 @@ Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. -## Enforcement +== Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at abuse@verron.pro. +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at link:mailto:abuse@verron.pro[abuse@verron.pro]. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of any incident reporter. -## Enforcement Guidelines +== Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: -### 1. Correction +=== 1. Correction -**Community Impact**: Use of inappropriate language or other behavior deemed +*Community Impact*: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. -**Consequence**: A private, written warning from community leaders, providing +*Consequence*: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. -### 2. Warning +=== 2. Warning -**Community Impact**: A violation through a single incident or series +*Community Impact*: A violation through a single incident or series of actions. -**Consequence**: A warning with consequences for continued behavior. No +*Consequence*: A warning with consequences for continued behavior. +No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. -### 3. Temporary Ban +=== 3. Temporary Ban -**Community Impact**: A serious violation of community standards, including +*Community Impact*: A serious violation of community standards, including sustained inappropriate behavior. -**Consequence**: A temporary ban from any sort of interaction or public +*Consequence*: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. -### 4. Permanent Ban +=== 4. Permanent Ban -**Community Impact**: Demonstrating a pattern of community +*Community Impact*: Demonstrating a pattern of community standards violation, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes. -**Consequence**: A permanent ban from any sort of public interaction within +*Consequence*: A permanent ban from any sort of public interaction within the community. -## Attribution +== Attribution -This Code of Conduct is adapted from the [Contributor Covenant](https://contributor-covenant.org/), version -[1.4](https://www.contributor-covenant.org/version/1/4/code-of-conduct/code_of_conduct.md) and -[2.0](https://www.contributor-covenant.org/version/2/0/code_of_conduct/code_of_conduct.md), -and was generated by [contributing-gen](https://github.com/bttger/contributing-gen). +This Code of Conduct is adapted from the https://contributor-covenant.org/[Contributor Covenant], version +https://www.contributor-covenant.org/version/1/4/code-of-conduct/code_of_conduct.md[1.4] and +https://www.contributor-covenant.org/version/2/0/code_of_conduct/code_of_conduct.md[2.0], and was generated by https://github.com/bttger/contributing-gen[contributing-gen]. diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc new file mode 100644 index 00000000..bae2c99f --- /dev/null +++ b/CONTRIBUTING.adoc @@ -0,0 +1,169 @@ += Contributing to Docx Stamper + +First off, thanks for taking the time to contribute! +❤️ + +All types of contributions are encouraged and valued. +See the <> for different ways to help and details about how this project handles them. +Please make sure to read the relevant section before making your contribution. +It will make it a lot easier for us maintainers and smooth out the experience for all involved. +The community looks forward to your contributions. +🎉 + +____ + +And if you like the project, but just don't have time to contribute, that's fine. +There are other easy ways to support the project and show your appreciation, which we would also be thrilled about: +- Star the project - Tweet about it - Refer to this project in your project's readme - Mention the project at local meetups and tell your friends/colleagues + +____ + +== Table of Contents + +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> +* <> + +== Code of Conduct + +This project and everyone participating in it is governed by the +https://github.com/verronpro/docx-stamperblob/master/CODE_OF_CONDUCT.md[Code of Conduct]. +By participating, you are expected to uphold this code. +Please report unacceptable behavior to link:mailto:conduct@verron.pro[conduct@verron.pro] + +== I Have a Question + +____ + +If you want to ask a question, we assume that you have read the available https://verronpro.github.io/docx-stamper/[Documentation]. + +____ + +Before you ask a question, it is best to search for existing https://github.com/verronpro/docx-stamper/issues[Issues] that might help you. +In case you have found a suitable issue and still need clarification, you can write your question in this issue. +It is also advisable to search the internet for answers first. + +If you then still feel the need to ask a question and need clarification, we recommend the following: + +* Open an https://github.com/verronpro/docx-stamper/issues/new[Issue]. +* Provide as much context as you can about what you're running into. +* Provide project and platform versions (OS, Office version, docx4j version, etc), depending on what seems relevant. + +We will then take care of the issue as soon as possible. + +== I Want To Contribute + +''' + +=== Legal Notice + +When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license. + +''' + +=== Reporting Bugs + +==== Before Submitting a Bug Report + +A good bug report shouldn't leave others needing to chase you up for more information. +Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. +Please complete the following steps in advance to help us fix any potential bug as fast as possible. + +* Make sure that you are using the latest version. +* Determine if your bug is really a bug and not an error on your side, e.g. using incompatible environment components/versions (Make sure that you have read the https://verronpro.github.io/docx-stamper/[documentation]. +If you are looking for support, you might want to check <>). +* To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error in the https://github.com/verronpro/docx-stamperissues?q=label%3Abug[bug tracker]. +* Also make sure to search the internet (including Stack Overflow) to see if users outside the GitHub community have discussed the issue. +* Collect information about the bug: +* Stack trace (Traceback) +* OS, Platform and Version (Windows, Linux, macOS, x86, ARM) +* Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant. +* Possibly your input and the output +* Can you reliably reproduce the issue? +And can you also reproduce it with older versions? + +==== How Do I Submit a Good Bug Report? + +''' + +You must never report security related issues, vulnerabilities or bugs, including sensitive information to the issue tracker, or elsewhere in public. +Instead, sensitive bugs must be sent by email to link:mailto:security@verron.pro[security@verron.pro]. + + +''' + +We use GitHub issues to track bugs and errors. +If you run into an issue with the project: + +* Open an https://github.com/verronpro/docx-stamper/issues/new[Issue]. +(Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.) +* Explain the behavior you would expect and the actual behavior. +* Please provide as much context as possible and describe the _reproduction steps_ that someone else can follow to recreate the issue on their own. +This usually includes your code. +For good bug reports, you should isolate the problem and create a reduced test case. +* Provide the information you collected in the previous section. + +Once it's filed: + +* The project team will label the issue accordingly. +* A team member will try to reproduce the issue with your provided steps. +If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for those steps and mark the issue as `needs-repro`. +Bugs with the `needs-repro` tag will not be addressed until they are reproduced. +* If the team is able to reproduce the issue, it will be marked `needs-fix`, as well as possibly other tags (such as `critical`), and the issue will be left to be <>. + +=== Suggesting Enhancements + +This section guides you through submitting an enhancement suggestion for Docx Stamper, *including completely new features and minor improvements to existing functionality*. +Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions. + +==== Before Submitting an Enhancement + +* Make sure that you are using the latest version. +* Read the https://verronpro.github.io/docx-stamper/[documentation] carefully and find out if the functionality is already covered, maybe by an individual configuration. +* Perform a https://github.com/verronpro/docx-stamper/issues[search] to see if the enhancement has already been suggested. +If it has, add a comment to the existing issue instead of opening a new one. +* Find out whether your idea fits with the scope and aims of the project. +It's up to you to make a strong case to convince the project's developers of this feature merits. +Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. +If you're just targeting a minority of users, consider writing an add-on/plugin library. + +==== How Do I Submit a Good Enhancement Suggestion? + +Enhancement suggestions are tracked as https://github.com/verronpro/docx-stamper/issues[GitHub issues]. + +* Use a *clear and descriptive title* for the issue to identify the suggestion. +* Provide a *step-by-step description of the suggested enhancement* in as many details as possible. +* *Describe the current behavior* and *explain which behavior you expected to see instead* and why. +At this point you can also tell which alternatives do not work for you. +* You may want to *include screenshots and animated GIFs* which help you demonstrate the steps or point out the part which the suggestion is related to. +You can use https://www.cockos.com/licecap/[this tool] to record GIFs on macOS and Windows, and https://github.com/colinkeenan/silentcast[this tool] or https://github.com/GNOME/byzanz[this tool] on Linux. +* *Explain why this enhancement would be useful* to most Docx Stamper users. +You may also want to point out the other projects that solved it better and which could serve as inspiration. + +=== Your First Code Contribution + +=== Improving The Documentation + +TODO + +== Styleguides + +=== Commit Messages + +TODO + +== Join The Project Team + +TODO + +== Attribution + +This guide is based on the *contributing-gen*. https://github.com/bttger/contributing-gen[Make your own]! diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 4bfd1a84..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,164 +0,0 @@ - -# Contributing to Docx Stamper - -First off, thanks for taking the time to contribute! ❤️ - -All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. 🎉 - -> And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support -> the project and show your appreciation, which we would also be thrilled about: -> - Star the project -> - Tweet about it -> - Refer to this project in your project's readme -> - Mention the project at local meetups and tell your friends/colleagues - - -## Table of Contents - -- [Code of Conduct](#code-of-conduct) -- [I Have a Question](#i-have-a-question) -- [I Want To Contribute](#i-want-to-contribute) -- [Reporting Bugs](#reporting-bugs) -- [Suggesting Enhancements](#suggesting-enhancements) -- [Your First Code Contribution](#your-first-code-contribution) -- [Improving The Documentation](#improving-the-documentation) -- [Styleguides](#styleguides) -- [Commit Messages](#commit-messages) -- [Join The Project Team](#join-the-project-team) - - -## Code of Conduct - -This project and everyone participating in it is governed by the -[Code of Conduct](https://github.com/verronpro/docx-stamperblob/master/CODE_OF_CONDUCT.md). -By participating, you are expected to uphold this code. Please report unacceptable behavior -to conduct@verron.pro - - -## I Have a Question - -> If you want to ask a question, we assume that you have read the available [Documentation](https://verronpro.github.io/docx-stamper/). - -Before you ask a question, it is best to search for existing [Issues](https://github.com/verronpro/docx-stamper/issues) that might help you. In case you have found a suitable issue and still need clarification, you can write your question in this issue. It is also advisable to search the internet for answers first. - -If you then still feel the need to ask a question and need clarification, we recommend the following: - -- Open an [Issue](https://github.com/verronpro/docx-stamper/issues/new). -- Provide as much context as you can about what you're running into. -- Provide project and platform versions (OS, Office version, docx4j version, etc), depending on what seems relevant. - -We will then take care of the issue as soon as possible. - - - -## I Want To Contribute - -> ### Legal Notice -> When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license. - -### Reporting Bugs - - -#### Before Submitting a Bug Report - -A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. Please complete the following steps in advance to help us fix any potential bug as fast as possible. - -- Make sure that you are using the latest version. -- Determine if your bug is really a bug and not an error on your side, e.g. using incompatible environment - components/versions (Make sure that you have read the [documentation](https://verronpro.github.io/docx-stamper/). If - you are looking for support, you might want to check [this section](#i-have-a-question)). -- To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error in the [bug tracker](https://github.com/verronpro/docx-stamperissues?q=label%3Abug). -- Also make sure to search the internet (including Stack Overflow) to see if users outside the GitHub community have - discussed the issue. -- Collect information about the bug: -- Stack trace (Traceback) -- OS, Platform and Version (Windows, Linux, macOS, x86, ARM) -- Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant. -- Possibly your input and the output -- Can you reliably reproduce the issue? And can you also reproduce it with older versions? - - -#### How Do I Submit a Good Bug Report? - -> You must never report security related issues, vulnerabilities or bugs, including sensitive information to the issue -> tracker, or elsewhere in public. Instead, sensitive bugs must be sent by email to security@verron.pro. - - -We use GitHub issues to track bugs and errors. If you run into an issue with the project: - -- Open an [Issue](https://github.com/verronpro/docx-stamper/issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.) -- Explain the behavior you would expect and the actual behavior. -- Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to - recreate the issue on their own. This usually includes your code. For good bug reports, you should isolate the problem - and create a reduced test case. -- Provide the information you collected in the previous section. - -Once it's filed: - -- The project team will label the issue accordingly. -- A team member will try to reproduce the issue with your provided steps. If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for those steps and mark the issue as `needs-repro`. Bugs with the `needs-repro` tag will not be addressed until they are reproduced. -- If the team is able to reproduce the issue, it will be marked `needs-fix`, as well as possibly other tags (such as `critical`), and the issue will be left to be [implemented by someone](#your-first-code-contribution). - - - - -### Suggesting Enhancements - -This section guides you through submitting an enhancement suggestion for Docx Stamper, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions. - - -#### Before Submitting an Enhancement - -- Make sure that you are using the latest version. -- Read the [documentation](https://verronpro.github.io/docx-stamper/) carefully and find out if the functionality is already covered, maybe by an individual configuration. -- Perform a [search](https://github.com/verronpro/docx-stamper/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. -- Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to - convince the project's developers of this feature merits. Keep in mind that we want features that will be useful to - the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing - an add-on/plugin library. - - -#### How Do I Submit a Good Enhancement Suggestion? - -Enhancement suggestions are tracked as [GitHub issues](https://github.com/verronpro/docx-stamper/issues). - -- Use a **clear and descriptive title** for the issue to identify the suggestion. -- Provide a **step-by-step description of the suggested enhancement** in as many details as possible. -- **Describe the current behavior** and **explain which behavior you expected to see instead** and why. At this point you can also tell which alternatives do not work for you. -- You may want to **include screenshots and animated GIFs** which help you demonstrate the steps or point out the part which the suggestion is related to. You can use [this tool](https://www.cockos.com/licecap/) to record GIFs on macOS and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux. -- **Explain why this enhancement would be useful** to most Docx Stamper users. You may also want to point out the other projects that solved it better and which could serve as inspiration. - - - -### Your First Code Contribution - - -### Improving The Documentation -TODO - -## Styleguides -### Commit Messages -TODO - -## Join The Project Team -TODO - - -## Attribution -This guide is based on the **contributing-gen**. [Make your own](https://github.com/bttger/contributing-gen)! diff --git a/SECURITY.md b/SECURITY.adoc similarity index 64% rename from SECURITY.md rename to SECURITY.adoc index ea68b965..6e87ccbb 100644 --- a/SECURITY.md +++ b/SECURITY.adoc @@ -1,22 +1,22 @@ -# Security Policy += Security Policy -## Supported Versions +== Supported Versions Currently, only the latest version of our project is being supported with security updates. -| Version | Supported | -|---------|--------------------| -| Latest | :white_check_mark: | -| < Older | :x: | +|=== +| Version | Supported +| Latest | :white_check_mark: +| < Older | :x: +|=== -## Reporting a Vulnerability +== Reporting a Vulnerability If you have discovered a vulnerability in our project, we appreciate your help in disclosing it to us responsibly. Please avoid public disclosure of the issue until we've had a chance to address it. -**Do NOT create GitHub issues for security vulnerabilities**. +*Do NOT create GitHub issues for security vulnerabilities*. -Instead, to ensure privacy and efficient communication, please email your findings directly to me -at [security@verron.pro](mailto:joseph@verron.pro). +Instead, to ensure privacy and efficient communication, please email your findings directly to me at link:mailto:joseph@verron.pro[security@verron.pro]. We will review your report and will communicate any updates or requests for additional information through the same channel. Your effort in helping us ensure the security of our project is greatly appreciated, and we will endeavor to From 64cc24811a6e7b27833f35f3b5e17882404ac2d4 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 31 Mar 2024 05:45:02 +0200 Subject: [PATCH 062/134] removed unused getRuns method --- .../docxstamper/core/PowerpointParagraph.java | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/core/PowerpointParagraph.java b/src/main/java/pro/verron/docxstamper/core/PowerpointParagraph.java index 4862e003..18250435 100644 --- a/src/main/java/pro/verron/docxstamper/core/PowerpointParagraph.java +++ b/src/main/java/pro/verron/docxstamper/core/PowerpointParagraph.java @@ -3,7 +3,6 @@ import org.docx4j.dml.CTRegularTextRun; import org.docx4j.dml.CTTextCharacterProperties; import org.docx4j.dml.CTTextParagraph; -import org.docx4j.wml.R; import pro.verron.docxstamper.api.Paragraph; import pro.verron.docxstamper.api.Placeholder; @@ -19,8 +18,6 @@ * runs a word or a string of words is spread.

*

This class aggregates multiple runs so they can be treated as a single text, no matter how many runs the text * spans. - * Call {@link #addRun(R, int)} to add all runs that should be aggregated. Then, call - * methods to modify the aggregated text. Finally, call {@link #asString()} to get the modified text. * * @author Joseph Verron * @author Tom Hombergs @@ -291,18 +288,6 @@ private static CTTextCharacterProperties apply( return destination; } - /** - * Returns the list of runs that are aggregated. Depending on what modifications were done to the aggregated text, - * this list may not return the same runs initially added to the aggregator. - * - * @return the list of aggregated runs. - */ - private List getRuns() { - return runs.stream() - .map(PowerpointRun::run) - .toList(); - } - /** * {@inheritDoc} */ @@ -310,5 +295,4 @@ private List getRuns() { public String toString() { return asString(); } - } From fec8afb507535226b2bf1daa1bdb0d52c7cb3ea5 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Tue, 16 Apr 2024 08:32:15 +0800 Subject: [PATCH 063/134] Add office stamper examples Added several classes in 'officestamper-examples' package that demonstrate the use of the office stamper tool. The pivotal files are 'Examples.java', 'Main.java', and 'Diagnostic.java'. This commit also includes a new 'pom.xml' file for project build configuration and a few helper classes. The '.gitignore' file was updated to properly ignore the '/target/' directory. --- .gitignore | 2 +- officestamper-examples/pom.xml | 38 ++++++++++ .../pro/verron/officestamper/Diagnostic.java | 67 ++++++++++++++++++ .../officestamper/EvaluationContexts.java | 10 +++ .../pro/verron/officestamper/Examples.java | 50 +++++++++++++ .../java/pro/verron/officestamper/Main.java | 19 +++++ .../java/pro/verron/officestamper/Utils.java | 37 ++++++++++ .../pro/verron/officestamper/Diagnostic.docx | Bin 0 -> 60061 bytes 8 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 officestamper-examples/pom.xml create mode 100644 officestamper-examples/src/main/java/pro/verron/officestamper/Diagnostic.java create mode 100644 officestamper-examples/src/main/java/pro/verron/officestamper/EvaluationContexts.java create mode 100644 officestamper-examples/src/main/java/pro/verron/officestamper/Examples.java create mode 100644 officestamper-examples/src/main/java/pro/verron/officestamper/Main.java create mode 100644 officestamper-examples/src/main/java/pro/verron/officestamper/Utils.java create mode 100644 officestamper-examples/src/main/resources/pro/verron/officestamper/Diagnostic.docx diff --git a/.gitignore b/.gitignore index 70b8d953..e0058495 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ .idea *.iml settings.xml -/target/ +**/target/ /.classpath /.DS_Store /.project diff --git a/officestamper-examples/pom.xml b/officestamper-examples/pom.xml new file mode 100644 index 00000000..2963ef5a --- /dev/null +++ b/officestamper-examples/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + officestamper-examples + pro.verron + 0.1 + + + 17 + 17 + UTF-8 + + + + + pro.verron + docx-stamper + 1.6.7 + + + + org.springframework + spring-context + 6.1.4 + compile + + + + org.slf4j + slf4j-jdk14 + 2.0.12 + + + + diff --git a/officestamper-examples/src/main/java/pro/verron/officestamper/Diagnostic.java b/officestamper-examples/src/main/java/pro/verron/officestamper/Diagnostic.java new file mode 100644 index 00000000..f4beee9e --- /dev/null +++ b/officestamper-examples/src/main/java/pro/verron/officestamper/Diagnostic.java @@ -0,0 +1,67 @@ +package pro.verron.officestamper; + +import java.time.LocalDate; +import java.util.Arrays; +import java.util.List; +import java.util.Map.Entry; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; + +import static java.util.stream.Collectors.toMap; + +public class Diagnostic { + + public static final Logger logger = Utils.getLogger(); + + private final LocalDate date; + private final String user; + + public Diagnostic() { + date = LocalDate.now(); + user = System.getenv("USERNAME"); + } + + LocalDate date() { + return date; + } + + String user() { + return user; + } + + List> userPreferences() { + var preferenceRoot = Preferences.userRoot(); + var preferenceKeys = extractPreferenceKeys(preferenceRoot); + var entries = preferenceKeys.stream() + .collect(toMap(k -> k, k -> preferenceRoot.get(k, null))) + .entrySet(); + return List.copyOf(entries); + } + + private List extractPreferenceKeys(Preferences preferenceRoot) { + try { + return Arrays.asList(preferenceRoot.keys()); + } catch (BackingStoreException e) { + logger.log(Level.WARNING, "Failed to list the preference keys", e); + return List.of("failed-to-list-preference-keys"); + } + } + + List> jvmProperties() { + var properties = System.getProperties(); + var propertyNames = properties.stringPropertyNames(); + var entries = propertyNames.stream() + .collect(toMap(k -> k, properties::getProperty)) + .entrySet(); + return List.copyOf(entries); + } + + List> environmentVariables() { + var env = System.getenv(); + var entries = env.entrySet(); + return List.copyOf(entries); + } + +} diff --git a/officestamper-examples/src/main/java/pro/verron/officestamper/EvaluationContexts.java b/officestamper-examples/src/main/java/pro/verron/officestamper/EvaluationContexts.java new file mode 100644 index 00000000..6572729f --- /dev/null +++ b/officestamper-examples/src/main/java/pro/verron/officestamper/EvaluationContexts.java @@ -0,0 +1,10 @@ +package pro.verron.officestamper; + +import org.springframework.context.expression.MapAccessor; +import org.wickedsource.docxstamper.api.EvaluationContextConfigurer; + +public class EvaluationContexts { + static EvaluationContextConfigurer enableMapAccess() { + return ctx -> ctx.addPropertyAccessor(new MapAccessor()); + } +} diff --git a/officestamper-examples/src/main/java/pro/verron/officestamper/Examples.java b/officestamper-examples/src/main/java/pro/verron/officestamper/Examples.java new file mode 100644 index 00000000..3c87c282 --- /dev/null +++ b/officestamper-examples/src/main/java/pro/verron/officestamper/Examples.java @@ -0,0 +1,50 @@ +package pro.verron.officestamper; + +import org.wickedsource.docxstamper.DocxStamper; +import org.wickedsource.docxstamper.DocxStamperConfiguration; + +import java.io.OutputStream; +import java.util.Map; +import java.util.logging.Logger; + +import static pro.verron.officestamper.EvaluationContexts.enableMapAccess; + +public class Examples { + + public static final Logger logger = Utils.getLogger(); + + public static void stampDiagnostic(OutputStream outputStream) { + logger.info("Start of the diagnostic stamping procedure"); + logger.info("Setup a map-reading able docx-stamper instance"); + var configuration = new DocxStamperConfiguration() + .setEvaluationContextConfigurer(enableMapAccess()); + var stamper = new DocxStamper<>(configuration); + + logger.info("Load the internally packaged 'Diagnostic.docx' template resource"); + var template = Utils.streamResource("Diagnostic.docx"); + + logger.info("Create a context with: " + + "system environment variables, " + + "jvm properties, " + + "and user preferences"); + + var diagnosticMaker = new Diagnostic(); + var context = Map.of( + "reportDate", diagnosticMaker.date(), + "reportUser", diagnosticMaker.user(), + "environment", diagnosticMaker.environmentVariables(), + "properties", diagnosticMaker.jvmProperties(), + "preferences", diagnosticMaker.userPreferences() + ); + + logger.info("Start stamping process"); + stamper.stamp( + template, + context, + outputStream + ); + + logger.info("End of the diagnostic stamping procedure"); + } + +} diff --git a/officestamper-examples/src/main/java/pro/verron/officestamper/Main.java b/officestamper-examples/src/main/java/pro/verron/officestamper/Main.java new file mode 100644 index 00000000..06faaff2 --- /dev/null +++ b/officestamper-examples/src/main/java/pro/verron/officestamper/Main.java @@ -0,0 +1,19 @@ +package pro.verron.officestamper; + + +import java.nio.file.Files; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class Main { + + private static final Logger logger = Utils.getLogger(); + + public static void main(String[] args) + throws Exception { + var outputPath = Files.createTempFile("Diagnostic-", ".docx"); + var outputStream = Files.newOutputStream(outputPath); + logger.log(Level.INFO, "Stamping to file: " + outputPath); + Examples.stampDiagnostic(outputStream); + } +} diff --git a/officestamper-examples/src/main/java/pro/verron/officestamper/Utils.java b/officestamper-examples/src/main/java/pro/verron/officestamper/Utils.java new file mode 100644 index 00000000..a2dd4046 --- /dev/null +++ b/officestamper-examples/src/main/java/pro/verron/officestamper/Utils.java @@ -0,0 +1,37 @@ +package pro.verron.officestamper; + +import java.io.InputStream; +import java.util.Locale; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE; + +public class Utils { + + public static final String LOGGING_FORMAT_KEY = "java.util.logging.SimpleFormatter.format"; + public static final String LOGGING_FORMAT_VAL = "[%1$tl:%1$tM:%1$tS] %2$s %4$-7s: %5$s %6$s %n"; + + static { + Locale.setDefault(Locale.ROOT); + System.setProperty(LOGGING_FORMAT_KEY, LOGGING_FORMAT_VAL); + Logger.getLogger("org.docx4j") + .setLevel(Level.SEVERE); + } + + public static Logger getLogger() { + var callerName = StackWalker + .getInstance(RETAIN_CLASS_REFERENCE) + .getCallerClass() + .getTypeName(); + return Logger.getLogger(callerName); + } + + static InputStream streamResource(String name) { + return StackWalker + .getInstance(RETAIN_CLASS_REFERENCE) + .getCallerClass() + .getResourceAsStream(name); + } + +} diff --git a/officestamper-examples/src/main/resources/pro/verron/officestamper/Diagnostic.docx b/officestamper-examples/src/main/resources/pro/verron/officestamper/Diagnostic.docx new file mode 100644 index 0000000000000000000000000000000000000000..8a1d0db7acbf2e74f6de67060a783f9670c0d3fc GIT binary patch literal 60061 zcmeFXV~l9Qwl&(^ZQIsv+s1C&wr$(Cjor3w+qP}%>vL}Im-BK?Uf%nCZzXH}S(Qp^ zt}$zlF>1<60D~X{`~m<6001BW00CR$Z3hGZSb+clKmq^<(h#(EMH}Mc?rVb>#Me<{Ztd8$V|NP!yP_KsE_Lqh*q{0#V9e0kefC@~HbgV5V?^zY-_+ z$P~}#Xu5~&*!m~^%9!@%r&#(`zg_*{CV|*XoUCPqL|N@01|OLWL@||<^|r%chHYO~ z;V>s2;wT9bdF@z2_|0N^W?jlGM>%pOG=QZ?5z+lp(sE~SRm!kUTOTl=Qj|QisA5jw z4nZL|T{+qwin7-D(av_jk=GovWE-V>oey_i$oBaQ+C*nkb~;dF z9aLy-a2hXMx($tRcZl!J)4$TXn;T!g7abtVp0*E)>ML60R=NYN@Bue#nBKV{;VKBA zSVZ46$pfLVIjIjEyz)*r@a?;rFDhh?16t&*K*SO{3=Um^0R;-!AkvIz6Uxug!T0k1 zMzA7z)kG}qk)1yS{@Y;nQmLRUJCGkt=vZ_PtQsyIV|{n>+++0v1_0pu8yG^! zahT25KR}lLNolB`oY%28vUH%O`N#WzbNzpCg8$3bE8=_2`{|(ht^+;;W;^B9deIA{ zX$@x9(O1F1HN+&5)>q7zzdpHER)Dll_5O^{%_q#bJ7kJD?Idd5U?wZUgm*$Ly=wPs zymz<)Bn5R8(Yr3$?m=f9JsE$F5s6WZ1w^Q!gimAsihBu7o$Nt6{4Ml!SPW%GNHM)& zKwMvtZKt&Soa)V(n?}F5WG<@V0anH>+!c(|Hih90Y0O)j!blw()3;TpHK2Hw(83U9 zPVq>EX-P**k{PF80f~9jF?;4-&|N$j7DNS+2N6d5?rE%vsQz^veuTlxa!sJtptEa( zm8r#l|5N$?JwF!TeE70|>R|*s006>IP+V;64QUN+44kZf3gbU2;)SY)%{mLRH(t%R zF1VMJ@TO6Ln4X5bN(Hb#>18%XSagtRzR0X|Wd65Tq0pIrYnbQgjQ(wa(cOtB7gtJ5 zi1+gsy_ATBP&m3tO865hIZ=R2hvLKo-Y|zw%w*86hxuwApqU7$DbPWOgR8AFbUihEj_)UUZ=Ykkuf~&uq z%fJkAK?f?%2-JoNE>|*hdR8yop}fiFP`Qze7YjB+~B!B_PYQ5I>tkj`siV z*6m?0yw%Daa2%)eqt?^S{3)F&H(?6P|wNWDSa3$ zSmGP4Ftlfuc>{gxu9^yBX%v)bB-ZG~U-mmy^whqlPVj-&qMR=Q<;^wWp(5k` zJBE>pRZtX~efcz)(V?*Gxk~+3^U2-!@AKhjz`f-i{1GyO?<+Is!K@r$x?y{kNFsLW z^sEx8j7w7eqr>meNUkY#5vrw8s*RCj;5RnOjvsPgLNSM2G0fixl_HsqiIhvlo4cmt zIqXQO3D}4)aYe1TFUiKB$?{Wm3ek6JNFbKH4k44$sxs(aU5Wsy(N4N=j|VvCq9ii9&a_43?JxHI zMP=~i%+RYA3$mG$f!dv~Lm>_6RMbT1w4Qx&rz|GkwDyU_tci_4eSHbb#N@+)R}~sw zFhVbN<(_p?po9U~yC$<~8$A(5@&|N=Gwl2mzRdDfT`|(P<5#%mL>g+Yb>&YY*G3)m zZ_M-aoF481;od_JhTCptk|7#63XF|u)9f{qm0X`a3n#YcTbUbamUb2@44E3twd}7$ z^b@DY63sdY343UhPl=jUGN{PY*VkJ;3UK4@VnQjiWQF}@6b)tBIURf^16UE+A+ z+YR_^)syv?STb9!S3+M%V=1$1@rz2W>5*tG4`DW!CGjQiyB#e!wL=)HwOwiqX$F5c zNuuWI0IDblujcQYz%b?XcPLFgbOaTMvXx-xGmotqkXm~rM64UMy3c5cit}93kW*b) zfb*;HvuUE*t9o6#zyS2IuT@#Bz4RlUf@&(zIPxsp>V!g4);kkNP`L-rIrXV&DKnZ_ z>&9?zfj7m+A8$_p8l)v|W?mtvs?Nv8SYlZPYqNSuLdh@V47)=k{;rS>1R;z(FZc3g zRKL+>kdcW%-=dKl+ejP2AoY=~C#>3vI8Q{{XIC*dPAApEj6w~~r8*#8Q10HC)jRQq z2QWE4nX#mN+=MxEZpZ1YDvZodH5aDkUFW_?-@!(}EYL4D*QwUqd{1U>de-WfEZ{l!c7!P> zT9ZbZ*Ut(p;?4T4ow$QIQM z;$x&yyq^7`i7??Y$)fn*eBi2wf!(LkC`zbVXoN)JjleY_H%OKv{lYl5V$33rC~tp(9aVNM&Qt4sU#AZtR!WQizSXfH&=Z`Q+K^d6hxs)UL zDFP|9w+SDJ14Ua+`)j;O>i;U`po$$7PakS+6=ZsFLij2Q*- z1#uWPg)OK$2A(RwNG}$?$f0h4v5lGm{J|Lj<23>XhfPNiUGc<6ISx<87NQz{^L9mk z92v(hokB%ot;;L{n?2&=^zU7l?X)IuFlyu4>Pf|o4@qH^IiDSmLl2dMc(Gkcj1P0| zYO9G36<(C<&iGk9%=!iUt$$a&+03W4*S#y6Y??WCVP$dmRrWa5xpj<}iQIJ=4^3Pq zdpLK^vd#YHFkn3zyN_#}PD#I|i=X=YEk6Nlf1xMURe@sFt}4N9p=ZQ=%&ykUUtOlV zlqddip4?5Eb9vV`y#_dJ*KWJ$r_qvX{C=1tk>t|Ksd=B8IrkLGc}Z+M8A+iT#`4Dp z=d2FZh6kd%u~-sqh-5YP}tL?Yml@ducgoVNUmwDh^8=~ z@%L)ijv$IZ6q=yhi1RpdNRGl@VKY(iH4WE_pFn#E%hVUYy$h3`tsv0CM<+5?)mW&y zf;L#gKPEvb99sY{=2D8l77?97xIq8&k>9^8&A=pXIsk3ms^nW_FmQ}KOnKX@D4xBR z^8G{A{+-OBx>`L4{Ls8dFaQ8-0C2#6lDYpg$+^&#=nIMlpO`$_XM~6#y;BRftJ^Fwu5>D4Brji< z#&`^|{Xil7*5Br8MhDD3ZEZwx{G?DzW*%5 zUj%#y7*QJ(A&Ue&LdZ?pfF?$y!>yPFrUeT>YmgBgSS)&qRz!KibKyPpWW!j`n*u`j zJH;(PW}jy_f(u^*SE2CNN*&bqL${6|I7Ede8yU(5QYmZwYK~5QN&EMi5?@3Z=lgD5 zgmQdRK64t2j-~dy&E)YW@rk3@(_$<4UsH z$(po16h!@}29NfN9kP*6ODu?&>uo z+G!JTxt8{6|09UAFcWtxf7E)yPfbDmnek5$8{618TH81pIs8MCG$gM_XZ*vQp?t)F ze^wL3CudFiFv0won|D>W?peU+K0mXn>r9p^_8dE;)S3S*-ECsEU6^43SiO z;UJs*3<>pc9LIW?6KhLsV*idbc`(dI8MnS_=b=52ZkW_tx^XEx@O*{}dgfg-LU>O( zX$?NEq$)nCemba~FoFfE3fJ#Xvc~QtT&<6Bg-iZ=@A*DQN+B82`M-BcUiL0_+mP{$ zV^VRXLlGnP`*PrU3VQOMN>@QWF<-9Xa zJh+wT3C&}85WlkP<@|Kjvy&ScX@gx6AGgfcB!v71fTm9eEL z)n+z`RczOTb)n4z4ZPwHFqJs2BskgBI$G`-qF1RC8G6!5>7chcr#B6B& z69f}o$EG}1Ey54NN--Vb(m(zl+Cd3O?XUZIX~z7$4XB0UOxdy&we|5I<@T`9(oEG4 zxTAi+{eR`Qk+tE!V%z?o*yfyf(RI8Ar{f6>%M#mfNNAUyZ} zZNbG?&;fVTc(n%g{(j?81CqfSKuUy=R^Ab1 zYSHoev{xAjB-2FC=Z@Inpdr@wTR_JUEU^G|ABTo78N@3I`tq3ssIVy18{ey z7zy3%TGeyt3$B^$?oA#ZwOmQf6f8P_CZW+to)Jg@0i!Eaik2)JkRcIt0F5~ZI&+A$ z5*Tt6keU+CoF(?DfbG3qVU2`(ZxR(l1A7H{j}K#43k^rgf(66-#FIN(ph z@DOFZfK5H0vy3kE`VKC3dKknyM2N!(~2XGJx=Gi@pMvk~M%O_^6^B-f<$ zv%JwtdVL1yeWdy++KB9MIm~a{fE6eI_JnBnn%RP6B74VqSFV&@!g{9ub-}j=IMrb8 z-bjuy83lN+sSPLOiK;x^VGU(C&q_ZaoBO69m60zVP9ShzJ*1uOnK4;fG9I>n(E}ul zmVsQjjRNxzaX(Nda|;pYc=Lu=Rf#uw+<3AG?;j8Ab&Fjd$6qyNdt5f?Zw~s}3dB~+ zMr__&c6l=z#we@u_m+*Whp;WPxEwAN#Rsx$o7(MS@g7wceopUMM<})I;^VVIgh7$A zJ?R@^IUt7)a~vYI?a!7`vlnI@I$Sh-W{o!d`xOyq$C{U{26uHpU3kI~U_Psk z`&-E`#7!?XtN%0o&>3mD;spi(ID!2aa2wcI{liN7=fS;^GWid14_{}Ua=NlLh6wpa zMWwCaU=C+tb0mn(fywf*r27hy=v_2lL*DC!FoDh~cmfE|eKo9gJIhsdT_(6T5DCan zp$0pdVG{KL;mS8@p?BjhqgBzzL)OC&XT}BD;m^G^eSDn$eg?`W4y0fQ0!h%${sl!O z|N9X>o?i%&iWUhIoaR+TtIu$N6$=wS(E)*Qy<|7 z^6~D@oT|(czvt^%RAT9|%qfklvOG7)U7ha3pml8Yr-idA8uBU-BejJvl#xgG+H}*2 z`!U?gQ{7Obt5KTlRcLOLyYD8HI9^wLn0d7D1wZq#+DLkEbd|O%8wS*?HvYMCOkr%c zT4$$mB@15;;25akvQ2v`IIhq<- z8PWdpP5)2#R!u4bixs^K?G0~W1IK6b#UPAz#embSJ|pmgl;n?_NVZ%B28P&{77`E% zJ1S(6*=e5NmOHr>FvLUrSGtwL8uA@l*{rMOXtLUxr7ne9zdGLZ``PTfcnnkaxQa9) zP&E90>R+5<_tHIp#9vGUW(#&#*sv1RV77qs@QCU8(TsmKTI=3-!BTdkL*@K^8)RN# zFW73~=duS1J9DvInv|^F=0|yPUj52Ls18 zrpemud-WahDcw;Mp|b@M8;;u#e0YJ+0tbU13A#98LwP(- zbfHa25fQSiUAzcBv(am&11DoP8qS}cn{nc9p4EC&0U%_t3pmQ9H>LWd0DuN!pU;el zessUo-qz4%mep=}xng~PEc09bSZ2M^qJ2kq1s#jH`sK~`phCaE=QdkEP^fmV1zgSN zR$J?5qul1zY_@|!PRf>LnMLyw&1t!WdV^9{QCYRYgR;J)4s@-+ zk_eEbNp1@{;2Azd7t4l+$fb{du}hu|Wdo@&X5>($jG=sX=ksKzBqj`Ug6M(+(Z&_(C8J55_t}-y(&? zPCpJ{I`EY>s0UqZJu5{7tB|^?Is=S=b%Ny}nhUxtXbf`AK!L=vv$*ONaKk@g45Z<^ zygvYNr6BH}sCMkgjU^G&M-_DuP@fjv4zn9)W$8oR9k8lcNY%p)?GCRYXdn7#$Xp#mt3PaFd_zR`XgzfDOKYZJ;HWDm zTC#8hC3Z!VP(SeYcP2!tDn@_c$p|;OKGtXeJ5;2v0|6rrfj}?5Q0BU&bzLj}b6X;s z$J+BK7k!-MP8dup7{y|o7sstZs$^xR9p?E;%!?3>9JsZd9tr;>PW++~u5KIirVBm8 zoY&J<7g5q;7hhdAM!gk;(Bvl@nL%jc{at!C85_64#0{az$|&WOm%Xy84PuS! zO9ROfzf`1#;iNVP_D)-JEG1zKYCNRBwNy00jJ5~?Wzgs-Il2gV1Gc|IO3M%kyKHgn z=!!J6;mTIeIM4Cx4ReCsEE?6h(!_zul`ro+Db^0ni0_s}93H`&9IMUa+!blS4V%7$ z16fkV@~sJGsW7MU^NgKosMb(XDET8Z44e8yTyP6U+hvn>Hj7*BB!8LYFe8*_SVs#9`dk$XU#oG_DDk(?ToEfW;;; z*^)h~uEbaYGo3SQJ21xd8IrzS|7OGrnGHh>5YBuHtNo3P;5o2^RnU6vjR|6277-UE%Fkai!7PgbBLe-dk8MJH^CP*8ywaY)z&+b=p6Z?}9qTj|tS>Kg9 zK|U7$uvC1tDxwxbnyvvGd$G`PKHScEz^qKU$*Oud*IDIsDRADDg=pi0Z@QoyfU&?Z zglb-{oUq2O4c@SuEa9+@VYgM>`&Kpy8@i`mYK~P+dUDdG`Re7Sm4(oYqU1=JgYdEU z=TH+u(IStA3}^!ViVg0!_VS}ewoX|?2mfegdy@S4`I3azbh!$ccH`XjBJNmjC|jxf z>x$qjm*7zZD{YzA{-uPxW<4z^6NQ2k91W$x<5Q9P`qvYvE=C$y)LM;}%`;|^b0;TN z>!)`4!V^}feD^NvdwqF46;%WsTN<@N6=J z&%1<6_D6#*2-<@d&v^e)eQO4W1+anw01(3e3x{(sa&$DaHu*2{t+M@d9|-@C`@r6t zd(HD^?8?!dk=t}avxv1p-_BV=Dz-(^Uu!VvILLh3DHYPTcG0lxH9A zI{TU*5~r;N`{}Y}%U9-apl(GIvYmWCYseN0sf3=?L737H9M|7n<^6s6$&(;Nu|f*Q zCpQ|2lG0x(@5F*Tt}lTef9)#u8(>+Ju5d@eatJyss-3&`x}HlJe+_yA;&irz*@XVteISBpM7+5(Db6f8q{s^+EE|TwQH*9bii% zaeEGq{;+tv&9w(D+f_D{=YngPhk71r2flnCXzd(_X!(}yL*$9+JI=~gv}p<7=JUka z!u^XANH3#*A%0BteySt3!3@`7-is60Xk5sX-(x~EyMuslhKd|W-lO2R=_3+N7|K>x z^wid@>N#OhlPE5uJY)Pa4js=Njw+swS{E^}#z}M)2%?pj%jt0Vg|mX4pE5}cuo;`L zBqsXxgH8+lr$O?;?1&Tg15myLsMay(v1QG)IUyuDx2ak-E4Bq6{E*85R0D%{-d&UV zrr#Y8zlms^Sx`zM0#Oj*?s*Uph3r^*V$rFGuo(PH+cXOh{fVlwzGJLBxO7NL;9}7d za@rKHUBss|Fsa2tqIsAy)S!j#DnY0krlYqt++(uhP`yuHkvbRT9x82wHs+;?!6GKGlpBBDuN_R!+pAbaP9*T zEelVS7V^90WNTRqq0Yc5+Dl#x=Nas~kmc)?lUPuW7uPO(ih4TZ;M3%ZO2pGAU=k#~ z=-+zQ!)$|mn;L|NL$zuY$yhHBG-<(DQ2|-WWR5VjGue3djah&1<<`gAxNx9?)}8O& z-f3*#2f*>1yo=~@5Vcd4A-lBNAexm*aB{c_slGT0=KX$%pEMuQla63D?fwPg@_V=e+5q6_uV zISL%&z)CetFo)_LkA!G|Ld&39yKZ;=HAQh>?Dih;b3_srFwXIg*C%8grZEE4kadQw zv)IxOq&Zo&V*{t|ClW?7(%&)gC5wN9TnbSp)oSHxwRT9)^pRIgRTLM+tP zJcW05$%;qv)zoa6%mGJb)u!@hsCBDCj8exiv-#?2uH#l7IyPZ<6Z-_;Z%2xb5edIg zJ4!ViX=OK?q}m5#)&g}gDi?5!lxZ>+`^=T-*t7Vgf_Se3!xZqcz5eFHq#Yl_ra2ow z#hX7^(fS6Q(G9DrB?iCdV ziL{2ZrE6cfW2TkpB6|^x;YDiXr`w;?I4-t!34`sBGx1>l%es6F?al_~{hl)1f?P^vFH=-!mB8mpWcpZ# zi|w_}=o_Y(|GId?_lC~rf8_2H+@3(PKL*jx&x8Hn-2K0d`hPuZ|7p}m#?4Cn7}P`C zpx+^gH}n!2OOA0)bCuN4*#1+^z5`EfO6$bLa?Rgc_~5XfH@8<^?_R?$5$pR*B6zx+ zxk`TVYLSGpl~=zSX=moEdL5A2X_d(SROa%L*`7GNxz)P*%DvSbzy{dK<&vx920R}H z!!P6N##{e|DWN-U%qoN{+C(B+XKe)&*iS`x&+tn&nJ}G48L@5y5pGZL`>t`?hND(& z^UC3S<)sRJ&<_5z21|hO0=G1K-hoxO^Ft`2PkNBwI8~0;=WI~s z-1M;fwjx1fi4bn|K<*rz>{t4VPmCNQ*=M7!2QhKn+(D^Ew^q%XiBz^5Tvl--^JkJc zZA(CN~oM~&`X?hi+$ty`94O;;d zggfRoWLLjGmq?kaaavupDoNNf`S*VIsq_;M^nZ(oti6q`!~d_K zWXArpxhnWNpi6LO(<$EgRDLh9@iuq4wzsNkpE!60vWbypXv+(h(`h(sfQ-P4w=4$F zSbQt#9+Nu2GD9H0VnBM`ulIZ$$<22FOplX3PYIf;uXc4~F^kuc5FBr&R6>?C3&_2xl-k8*MiK}~ky6m~RMMNX>tNO64I$@|c%iT5W;{~kY&g^y2FtRMb#@SpI+@LxT~|KIR*p=D*W&ldmX-SY|7_pDZH zIBL^th#H-B0g~a!h2t{40~-QLJzGmtcQ7kK);jq4OnDZRXej>ofK3|s6vwjJif2$? zsa92~utewUsm^W&ZV#;KSlh9Iu`wXj!aeztN82&qwkVRr=lv0lk&Bg^mDsw)bu%8! zL5c;vH|4wI#Bt_VeYZnY*A7hU3Dt=4`A)3nIxIzVOPZGB0-935cWFTRs-^R4cN^x! zP(i6)q|>9l>BGXU?uG)blM=JyW1A0q>S=()=b5{uCZ@{%DBrb=$#Q2Hi~qKW(c5;t z9E`_djxjNvmj~7muZ$NEHesaZT_n|XB+JZM1+*JoC$(5e+E@fEz_a5V-pQFLE3`!g z=6Dy&!k^Etm4(XQ8EhAL@D-S;$(#Io*#1kmW~_lds414FQxCsp$Z1KN`A22Sb>dCj zk^FW7f+pM0_SP>(0j!PSnI*8R&lytwk4<7|FAt0$;**cJ>+h#=3EeER0ol;cKZx>& zpcPTw8vQWNp4$u{lWj;RnBy3hFrk-=~AR3YL%3wXbW zbQe^Tg)}IXeO9i^|FYwzk|MQruDW}NYSLI!xU%*mg~%F2L1;SKV;jNvzE=urmjKcW zCv+30?p-*?e-C7&^`7g=eJt2sg=8SZuHR9$7taF%jW4|^KxzHKM{dxOpvWt8f2tSnVKQ%dR~J$TXzl*rG~(V#Bl{A;NuhMe$-$b zoEbqeSO&M&7OroDz=6CUVbU5rZ{#Knkc<)@C6H@+ba4T2QKfFH%oI~rXGjb)<{Zt= z)dnkgtN?0tUe|mJVM1T+PTOeMe%uju*a;q^w#Vp@PqmW$!zEEu}U%y%MGrWLwjw4F&jrc)$A>lt@VlKtTjVB#37Ub}M5(;&CUBi_kS;pPe-Zzt7R0o9U=mbLi@PiZ$ZBXL# zw=VYu*^~y1c%_ckboj)wYl83RW`^vv86{%T;Wun?^11VJBZXn!<$PU4qEqC%Q7PL@ z7E$L99xG#~5cMWfg%XALCJ{qihnDiX-}a(iqp{QtP|hjr)fr)ATv*C)DLzfum*wpo zI8s3T)kq5b%@Heb<^x+4KT?E}9s z6F0$7XGsz1a4SeY3n5+6WY~qsn0JYSGJw7t}7{SJq}goG})_ z9sag=!{!P;mVV7Wigz#nd>6iqWdgwVpIsKnQc1ql=eyD&?D;q2dGXDS-YN;I)aEEi z2#4ufezPNmdiOIUBQ^DUSgWy#;QsKFIA6e);rRyi5z34AOY&CIH_T} z(7TUc^I(B=gL$plY|>a0)Y|dCdKQkEsd$a(o?H^xd}^avB+%BS6@~>E({;~!}2(nvGDyyuoToc8Y2eU(1WO38Q|uHw2*i zq{JKM+^;*_<+`pj?AMu7s+ij#6g3b?Z%&Lr3J!VCzCz9AwRk!_RH|C^V_k1YZf;gy z1mlx65w$ZPhOS;PoIcd{! zP+ss&3`H*gJ<8rTd?JVsCQp&}ZV0@&$_Ey)=h5=O2lz!qGqN1R4f5uf6^e|p7r$cl zr&Io5uA-)Dar4>j@>)&gaJgBWNpbxMN(!q5coZgQ`q@8rj7CpopL6|8@|YVVD}?S7 zAt--h`S5{8wkfw=aPf1}%f8cul`Q2i19=XDF5wJ`P){8B1H6=_Y?EXG??tRN|58NZ zipTsc?TCbH&rAdu&PoqVso$_@B7cN|=6bU526hY6sl+F)Ifx^_4={aAOQcx-HXkCp zmHT!6Hdk@>IBlRRpg8M}`MdD8)wCF?zE-=<6-#-15`xW9D%7!mDmyIkL>$m8 ziCcqKqOlCS$47OL$?ozuDuzLAzaUJU)iDkoCDd_dDs*riLyJANABelTO+^XjN3-mo z<1)zaPm>LOP&?}2U#(H1d6D+mUdRN4YB)$UqRM=%jJszbLZM2RZ4NlN9AB=FW9gPR zG4p?1TMdFeM;OFl{-A_FP2qjLQsaNY9HLM>D#d-Nep$l?SPLiuVfNAOX2St$1dhSbS#Kk3Yvx@7H$Jhc|WnmYYCk45WytykmjaHJNkxZ&y64L;vT+DUZ`?)inJY*@h3 z+BqiBXAe~jfG9f|Ru2RHPaVHfPJDyoU=$DyakB=@=nyX)%Kok*%HXCmN+P8G?uEmV znc?6jrjswu8G^4PzhE0Q9HjEXYW3i{K6DON$^1w#Ou1xHM`ph$rq zc1rlteIWyV+r;#I3{(@~6T56O-At_9QzlGWRbeAl^V>(EYV%lyq4K`GFu?l4#bxT) zK`>EN9Fc0w?|fV!sB6pP>XJ*oC**gW_RpO@m$<(lw;+m>v;MY%HDv1m?5z6&KAO7tL^njsOhYmTd}||3S@fAf;Z#V{rO@(l$d$jG*J)* zdYFbg&%|QF6bO|kpSO{4UaEm20y$G)bo~}%=0wk!;^?|S86G?S5h_c3aB1Mf(9rlJ zpT{a%lQoh7j?U2sar!C~g~K}D*(ATM9E^n3#~t-{$^2}bJP39aVa?t(k7(eQ23{k2!WI>mm zx)4=(k}&Taa1)a}A+oDC;6&&9={I_{6GRIK$n~5fRF4;c#?iU@aL)xqN@ompjogjX zlL5}yzGLdw8(JZ>LXtO1Wg8X}=YRT7Q(N<#OzC^-U7%nlXXAo=Qi_`w5 z%|8aMnZ%qc%-S+dJSUruH!e4u=oV zlw=|19qqCG-d8l9S2UqXl!9I^itTWt4&}+B5$Kw(xFWg}`Cro4uH!Ku4j7__LYlui z45^6xS{~nH8Xe^T@ydj*KVc(pTu9m2{|Mn&N8aZtUq8-o{+%8glQu@^qcT zPy$lVY>TQyFgHuqJ*|lk$TUxrVXcD@!=!_g%Bxb|+^^kK+EfGK6bXy;fDcdhL@Ph6 zyx?>->1>hbh8mD-h{+*CgR++ggemk?Q{~|CMgci|Lrve>GkbOg-$AT46zI)U8J|Q5B^&JLIr;MXwM^7wPnvg#V7RKQ3 zQQ<$2NL3rNU?cRuY4i^SVnrxGQ$*Yi?9HIb!uI`H1HiA0+1)GZ{q z$!Q=^VWhOd$%{0zxW4KvP{Cc$cU!@2A+SDN$Bgl3f$ij?Heh2O?9XbiAtxX_v}E6%458ctDNUY(VnpWrfRs9W#J& zV!4f+w}aZ!A50B9!VaDX42zoPVR<2UORYCer>B44fXj=rGon9w_H_nb^lJFwjDAOE zhplKWfD$%_(>l58PhNYS+S2;mc-7nw5%JDWT2ain$c= zU&NRMB6|a&KyF)(Hj*mj02+}f!jtqz6OXs#hHvrquq)nbaJK3ij)B+ezIb7ilPZva z%b67WU}LJ5djL|Tgh-x2-2v#gkB)8`w1Q1sxnG?hHv+sPO`N>BblOLa9>nL?CTe<4 zB+UsX#XRFxRd`h_mrI0xxgk5Z-z#fXQKm&w6;r;jdL{jK1Jep?NtQaUY)Hh(=j@6vLr;-c1pGjvpuF8k8jBYryB`y0&E4WxLx~)xpoIH684J(p|J@Og zQEEb*_JUgDk@Ncd@sXgfe62am!A;b-GZGWX{x)6e6lYNLnNHiLitt8eN8?O;ZOVxx z?F`jvP>0-GQ|Ypfy1JAr22WE?Z6kY=GqXcSqmX(OnYGy@B~A!pa=*m#8{p}r(!XcA zkV`#`M7ijQ*ul1{HE^rFq|)C>EbDNV*RO8{DlIID`_YVIWVT;Y zE&B|z6v_UfE)zCJ=k07&$q8+UpwjS6d}MNX?L|!DtbE9nl>cK_l?CdO!Nf5NN?OvE zo`Y4p;jRH|u19f@{f#JLzZ66+Vo=+P=Em`9M9*k@Yl6a98VK>+5SQh2WB==4GH03B z6&joiRYHhzSPiI@hE2qefZU>h1Q_+UBZ75druwY|@ceuNyNQccd(M|ffE*JU z^;WcX!2P0Pk~?S8xD8al#;wAcy9f3!ed$-orWGn!bwYP)pz~?ko2Kgvwk{zoAfbrs zd>n}ZPl}q&bJK`UTu*lbxA#qcffgN-YH4J{-v(iw$y}g*T(De~D!tp;pVbT!Iofw# z9S5JE$O44&>GFsfd`7xkAmQCvY-=qcC#{ZrbQpy~fnv2K0LBVMeQL<=794Z^`y(Fz z5R7aYw2?}L$&$HZgZkHmvRB6o9MP-2Qz|c*CTj8wj!~p?_6EbSV@SID%Wp}DaxGB;XTS8sB=GPJ z1ma58$XpW(i(WY1S1vCHg6TsB7D$%3 z)a$_PS6)c=9+0P4!YFC^%VW$yHL-uEu(rTkU9qzaz#PrvW$SoSB>WW@dWDW=>sn%Q zZs)AE(-iT$jK|;Uiiy%8tjc-MMoA+`U*-LYZKscOk@l$8)x$&DMYEWD0BbF(b|{V? zzvw_oq=+TFskly2y$bfCR*p7R&nn#d?@n~}BydBho;CJ3n-{xZnsc2(Y#Ksi(67kX zgR2gXqVvT5Dy#PbR!DE3#esoF)6Hyn4{7t)ko;>Us4G5HNvq6Kgt*fe6uoGdr{0Y~ zcK%CxgD+n}G|ov+@z-Au(ScSALlAPY+7KF!h3P_?+W85aBJJC?OZ2vP>3aikcyvJ% zT=j-@i1cni!q6JX{n^Au&F4tNx^Sd+wKg8iSjU)^;0>$n)dkcm%+suBq1>O1uhOro z!-mXER7Se6f5PeK-p0UDL5r#}`tyts_0x}LoyVdMXU?h{`~=*~v-K+^mDKi%s-QV&FNx-`n0?A#(7txh!{Y6Q3UK|R^3Z8Qla&g40-N#~iua=9x*lseNb zb)t2CdeFR`-foWnX>~=pIh*693yDte+Pic|czggjMW>e2N$Nn22hAjeK4CPOGuG<@ z6-7&10@WNy*_l?6MJr%83n{jVst_V8@bLlNxsk0AN1Kz4h&p6mL#)C4_hB1)`sG#YKKgnHGmC||#>_*=nMKs zrRNz9X`bAH5a|dGUFfePSP4E3jB42}VE7#b700(9^;yewk_wU|N#aaXXLm2UBVxhQ zqaOA&2GBY=-sKPX#rWT#&IQhi6C$Jy%MR3jlmN8VOKn4SD2dvX0nF#F!`A8`h82$V z{#kKuMcIZed?enxw@G;6NCN8eE-u&S^92C&v2X7Pd%!cIW3koCjTHsHt zSF*GH5Xz)iN}=+}6Jz!`>TWW7da)>d%ph6XCRJP&CR!x91D}BdThbu2anWzr{IO51 zzSQAabi+!T-x$h%+UZ^99DTuP1h)BkJYABbqzO;*MMtAH^~tU;EfYwO(ki@`9N(l)Tp2DQ9>05_mq~wa;8ZEG1jZD1mLlK+v~9ZZLD_ zkZa(rz2`|$vtE8-#@0*H`3Tctsjz4T+JWz@ix5ySg>Gmj6z!+UToixzFyZ{t-uSWW zBTuse_|PYhkB{q3xc>sGR=yu^UJ7o&*jQfMRIEG<&ct#Ul8+naS{-oG4=}WsCi+)7 z|8;WHP_H}cp8(#|QI(j!W80L(%%Fl~16J9Zyr?;+rZzU!rOwWg^v&P0aFrNQh!L-h zBcN*AKa%eqYDo;Exj}RM2KtSU)>)3J)djQOjxFMVi*$c3UaXRf! zyo@y3H`Qm)PAeBJCG!>2)S5qFibvG>d$OCg{~z#uG+{-R7Z+9O-rHP6$oA4oMCb070;sx_|Erj*-S^mxk2(qyKWfvUUfdY90 z_cC=aH)Lr0i_ZVX`CJx2Yt0Y5;z8qOGT0TkS0Ff_8m4#iPwr4#52d;X*@`BkzKQ-; zJV`fK_`&*~=`)xp>``@X)b*YZl!Ro*5-Ev?@46mn^^hh>pJ=AN(JT!f^XMdx=US}OH{9$R-GtofdQp@nMwEhd@ybo)! z7R5z^?12W8WMbR4HL|1 zeQVwKPp{smyMDi_I(zNiy}PPAc0w){l+D+K1maLTAdZDqo@++c^$s1T%A{JhkRIZ< zm8KF+m~`xTBs!s48}u7fP{Q=E|4l-9u!6gl!bq-=trlHG|C+%)c=aIY@(0g1%io1* zZ4=UfCJHg94T5!^FXe<_90;I~Yd&$xj@FdiVq1dqcN~@~u&;)2cr3n&1T+`!w!HV^ zepWn9EEJC%xM)1{{@Fi4yGkx|GKi69NT=|n%eVmbaHcV+D3YfmNoY9^^@!9 z?5*q~kMU1?Z6Squdi2-u`U;k{RFBrKLh=w^n7PnRoZ^t0JrAdwi}L%T3c>9e?q;EB z#c$uY-5mgNmn#GII##6+8)P1Gbrvx*7Rr_8P8o;ot5>nNVT5EbvlKr(1s10>5 z1Yrr+KxLcH%ICmDjvB|EGh*$G_ltXow8*3;_ptWv{Vql{JHF3I zbDvrV)zrTbU432jF({3cvSb?G@W%D2_7=z2rkc(H6UeGS&ae>w;w@{~wKep*zaAwh zyV7_5+V;HlX>gG=T~3z>&jtbSM`xCpl+F?DT)z)-7!nU)*x%x@K&_J^S_KI42#*2|a=idXKD zj!k|gi;U#kd6Y@}CK-C;2KEf=T0QD-K6ilf+ulNz7ck%Ee=gM4j2uI?1H-2!DE`wz zE$jbfp>|wT$M)nK(mQSN5<2`eqFKU0DD1f|8C7xq#%nd>Aa=Lb(B!JT`Qy^%H~*`d zhm*odk%Cem&8+YtR@2Gw>#mOe_6Im=R_e;ria3)FNZ--mkbNgU_Av3L5BjUPGSQJQ z@%q`8&B8}zd##-;6fHNra%xDZo+zxP`H8R&!p3JK1HOt^s}lzSn0~~_%>8RQ^A5?F z6ENBLZl1OLd@&a0Ne%5;yEAlsHcUD2$Y_maC@<~`rUt~Q2C5iUyK*5cU@Cerd1OoX z-BDz6hDau|Uo?C@F`r1KBt&sNjBFq5$5<$x?p8gS!nWUO~8LsI(IjGL2Ahaj&LZ8$WS@eJj zRXm;0qdCXP6kQ=UgB|GH%*7v$+28duYyVL|4-0X_AH?$W_N8+1 zz*u~YB$_6zCcAU;kvep0L5>xlw5>0&q70-v}c@jZee*&SsqJ$1c3Q0m$zBs$lUe+Ug!cBxu*_Qw9V{$=!E&en!`1vF?iTWOmzAL{Pwjj_rqkS@EQNt=G7o!?%gBvbGu+bqY|! zMyLk!rh+3xu4xZ4g*+8n!TNQoxKs}RCef?>RkO&nsKRFHk%KVCVGC?`F1XiIU64^wg0tr0aXn+w#sF+VR8h$T`*siTQWJJ9s(E z+|*eb{$fMSJ1}^AI0L{L&z})b(%10`(a{@)%qYl?PRX~L;OIRx;IgcW(q{cLYVnhPW+TX=*Pxb}3 z|0a|9Sx9A*r$s)7k34Dz4ntbQk*UGG*+W!pjGB)PU|{oeu3ND9qwvaR4V5#+NW>ykF>K^67%(^?2%9(-F?FY> z{(^!4Nmp}`_k%jgvWGlG8f@npgEnTH!qZ5}jzL_655)4|aJgOGbGZzjsD_4}F&Ukj z3f2jjYWNW^m-Jwq=elXu&}VW;BNOlExblO`Jo& z(m9bx$`;?fsOfjNv)*<=4Dlfj$I@E?X7`1^yLQC&7L1ME=weW^arQh*6~Z3nnjp5= z(K7Z>sD?XAxvX{!G+>Sw?|gP+&y*}F68ANep(Y@cC(h}29Y8Im;le6{E#zLWnIu9n z-^@=ka*fUotjny4wIpwd#Q^p|TbX}Op@Oz?t1mVi>!8cki30%@j8iM%i1$o1V~RDa-`}1Wcb&^XjtjXspe9na$1Jq` zxk6>lKsJonTf^QR>S)*X#Z>b;Y;3O5y2m@BWd=)HsryUtboTl^dW0V!R3Xsx&%TxI z!r+>7TVsoDu!XBg&}uAvt9P zj4nPYkeQ&0jsVTXGovenA4}|LRRse+`?sq^UHzrjKbR#4w^xFiqA$AF>Sd+M0rl=Q44Zm%8 zMx!2y&d*AcQUPLfyRT_Izw9}mUR#JI!%LoK*tnX{9KGFr+iMsx9hq-fq=R9W!Jyd( z4nWaY_^iY!vqF7cV(bAiNmvqEVd6v9Dt2FP3wQQQbSKEsRadp=#8qvU^|c3ZZcweK zBTqMbdNY%JiHfPaERf&3J2ymM2)-fd%+gk{8r6#U(6N0r{WOk!Dr?=vo3i$Q8$Cg?(5q8#;i9}N8MPELHtDAjz;Iu(f1W-q8r@U^D7u@{wh$yb z=IbbnRFvs1(SARgf|umbdKQw_Rp??rR>V141KSI(%NX`vNPq6;ckgyN0(*2{frHrU zc%-Mc{iN0J`Og`AA-Z_OWWd4D!T%op^?!OZ=Eo>l0=L#0xb*W3`qO4vG|`n%SRjRS zWE~Fg?1Wdq-mhA{!Ho5K;c&#Tv20FxO?Kz5U{>e8WOxU;NXO89u(BXvO;SlA}ZGu^0Amrgz3KQdlIw%}V+vrU{@t;3Rh{qBXM@ZB!q8 z>pw`pb?I8Sv%vC9rdZ7Zx+`t};HAlWu!mi52bNK6#ZN82PnX# zTk7dnc4{TBdef(3=2zJ6)?WUw8onc!To_xClxlvx->+=X)FSGn8{#;7T@wMDtosSu zM!o+y9PM$c$+w3A0jW&+x3_aM8>_Zu4cwX>-QgODu1=euq3Vb|p3h>hB8vZ&Z-|L3%37#F8>*+}0H(JqW&MLa_!{j~=ng#|dL|oP)+Tcy@ zw()G;A1M5+%@WB5eOMz7(P5N>o?Hzf$Z$;UYneKqeh9f0l`r%$yWsyOb-=V78biDS#E+bDgd94YmFMq1yrFPcSdarh6`(4Ni#rlmIo9DRyE*P0wgNy+8N}xW zVQrOyZ^AjyN|)%8{qrHR80d^sS$8ey;}k$aEI|rS8_>eQzz?w;9?>??hHSA_ArKy6 z2CfwX7c>vLWmrr>UJWU~7Gv@rLGiuA(QcT&2HDsJL>=5|Q#uf&*Tdn29yzrM<@viq zPn~%-Ut+3sDeUZS`yV`tygEu(DbyFufzl#Q-ulNpklyb1a7CBnwuUJ@DyDyH0@W84 zwieDc?&THyq>PC4s@Cf;ZOnK{{iqd?H(N~gBEx4t4Aneg5 zBRJkCcO<=>qrYTf)F98ijUW(K%#GKzzCJ>NUalduN+UFk(E$~Z*vF(p!rrv&)XVaf>LgWvccm_Lq@Zf?` zSy_>Hq`-uT_)Cf{g7l-z`$DI>Z|U1pEBh;lDKeEWGN;>B5%pbcp0M`|7LMK-3{1XV zNGLC17X+Jn)j?g2vmdk97Ga5Bqs|#UWxYY^?l3p_+Pm%uK2}E#`zk_mDkBK=>_oja zzV9)#y8N;wcD218B;TiB1NqUwtT1?XRfD=@giYxTZf z@+L|L&zSmN=Hw+G{9&KGJp$bMBsRIdwkL{ZRrV6k;0iB1N3wPEX!~WI5~ULA^j>qT z@-}lyz0x+Z=ePEj0_2wO=>$RLl%Hzvy;id@u2DGN{X{5t^yw@0iI!c*Lf?IhxpD3$ z2l6J}?Z+}u4C7E=drKyL%XjxD;?Uv7j}kAo{xW05qsS#j%eyzGjoLWIG+u>A7+%I> zy0)M?t7oc>VqN$3jtqRy(Fv)(%!fcwx=w3@Tg?Dn(fh!q^))CsS*o zQUl6G3c_sUx&y=JzM4$zj1ZAxHLb7K%xWRHeW+O}8!cMg^qBJN);I4oE)HlHtYz*r zzcKfhH3)UPw{XHf;as{PU;%y!VwVeA-K3kn;};&7WYoIfc2p!Dk7>-6z+XTtWneE= z(<|$ZY@})9O$fzVyq8*H!l_e)-uB3|I>OcKQy9<2oA&KY5^S|5j66Qkg5>9A)h=b5 z&VRTjTq5~C+XS}OS#zN)8?UKSRW$^pvhEUJQ6nFL+LLeMHCjc^XUcTt-$EW0{1=PTFTIYz$S&jm0 z>9zRAj$Ri!>o~XXqO6O7=31pTxP|vL)|Pu^^>XG~0o8wd>D0@e)Nq>_;;yc})v&Wi zEQOV&4&nkf=Gh3yD+{mlo6F33y)2vQfeh-}|OvaFB8-aHZLbmJAofbz8xKVFsH>V%%JV#{iz5VMtPYh120P8i? z$I63!@ne~{*Ptb6!`s}taLu~}s1jsy6+|Bkp|#Q03>Ag=q_(omLTnTfd~3j^rfy?v z6D0iV!U_6~i_&KK%|fe91s!kcjZRJVZ!HLIwc|LqEx2PID-fNIX{Ik~>?o^Rf955R zAbzr=aKpK z6gKDlo4`YRmRQV|cMI<)-bfM(;7cJD9iq=~yHfqE)^O(WjBGst4$&3}v68g6nl9!I zPT=MHdT~vhJU=c4ql{GmfRa*a06>?svXlk@%tcfOBxt|ns|GW3tTnUc_jtRJGdcGC%XODVGqb3}9 zr%fGrKpB>vfD)zV&VviqmM?${0xw)YPC&C+CkTv+RV|G194v8hRwb9pPEuAZP>-e6 zqzYqY$y3lTq*)S-KC;M^Eqd@_v4Z6~SzBvkp>MfcR1Xip>z}`wnwQRUf=7UVzFvC2 z-)DVdd0;)gmbhz$vwyyfF?yuC-eh&X^A@MP9Jk_Fsv1t=ks znEMy}M;9B4wFHZp!>uy!Qx(IaZG?@hJ{DWRz)u4QdDtSd8$seal$}D%Nd;FEDqw?a zQ~cGKp0}tgkQ=1HyOGww)IzLAKjXFF(QzoI9j(@?@@m=R^hB(~Y2l~63&}$lN!ZzP zIUp0o{LoW^U3PJV#4Zj`#g~dxF$RZO;D3XPf_5h@mgnFgt$=8xQt5 z;)BEf_}wk6h<8=ngdoF`qYt7;F)gZr%Q1$*R3nvIG5s|`6?nsue_a3P52nn?Ka2S7 zKtL8lU_cOoW7vP}&*b3fYW2^}na(^{?03H6Th^RKj-*Tvj)abs zk4l+3>CK^7*TqsSDzWSmTED^s+$>&w6u&3U{yDN53QAL{!2{Cu?C`b@?1 zg4AUJ`_j8tRkh}##u(K1QDwVDv4(lMAoqcC-ht%tgf>uh0s4k5(Bm$?!N!_8ggy_J+sT?|8))kRQ=EnU70SEc`H3qC|OFM5%*AMk~3f^yKu|LkI z@6mZbgUs6@Y-1hYGVn6i(j&V_kBXfFiK=;|W3Uuzy}CbpmY_99E~?#QNRvj6=slMB zni*|wgq800Ais@`yn6(^d2M{$Cmk|{ZVZ8Z2g6|llV%e3-}VM&-2R-d+C;p=Lje5% zgM7H`Zgrk5=rj4f_`GJi@_+6qWQb+2sbSCHwJ((96NkMQ!)zgAs)L_g@&z+YS$B)Ly$D z6z(?{qp(|VA0UK$V6B&x>rWGXV4Y7Gh;;7)%$?5;*a_&O+`PDZ z>%!mTxbm}hgzLGCd!~<7i$3EJXxt>=CcZL1625qc(fj(RDe9x-=iQ3{ivee{Y5Aib zFQ(hhj9ki>lTNe{W)D(g@v%Zzx(IwvnbI67EqJ*#3$sO4ktS<)HrukCwniLbqg1P5 zM3-*GyvwT0AX*vN6?F~>F`#|)4H5oI9hbK1^zUtnQ4=15#a@DfCE5WI$?p(5;~atX;O|K$1&BxZ%m z4TtY}YG_%ep64Q9tDR`ooz$fX{0eJj!_=_8-Oe=G6I+Z_o>1eVrt%8;Lbc zv^K>)_loRH4mRf^S9ax7ib2O}ASAfQ*332;ZMJs{9H%WZ9qzVwd!cwh&L3eCa<_$? z;>9CGl+6>1_YZ&QfsZs7F0-}15Yp?Dhy3&O(_kEK*$Ce3s){KiV25q{S-xZbaycZi zQzM}xcIHAq7Sat{bgjx{{pvN#oe{G7tc-)462|wat%+6QdK*ciq%wv=K%4}b<$2=t zV?fTsKLLiUU&ON!UH@)o!xHgG`OixZc8m39d#e`{OOjCg=goEBkxYFM6DrFfT#vEi zH_@s=vz?S~@8`D1rz#WfW)PxpZ{}w8up)CJ!ONqZNJdK1Gm}W<(IziBwq$`MZlC+_ z9^vc1OaoVomStQ%@pwhh2-lZ9QysCY>HlDMfqxnj;R@e<91QZOW3m{l=!GMZ7Q)Bv z?h3hO)3XYBV)ZMT99>PLQ2*o!?xN&ACQnPSBY#~FsaNk|a;RS(eZdPBxYp;!G75h5M)*wkav9UFU;@Oe~S)oYGx5?5TWt- zOl5 z9MXfl2THF^sDkjY-cP+Gdu<>pLAZkOa>BKgcvscVoEZ%bzZ|LB zpEx9WoF}q;_yh`K>tdYeFIoQaIZ9Qu*ferttKZF0W&@?;qB&H!<>FXXfU+V5TWZWI zsiC1cvnv0RVS_doDUg!HrI8a}{f>?d^5{e^Qc&`*>%Rc06fq}((p`+E#dohXfzrx~ ze*x-qX8%q4UjRnGmn%p#=toyx-Q_L)%fs7Yff+q?oxqd0*j-5Faftu>@ z+$z$8>wf57Tm#X!q3)<@Q4Z7VjQlVTc}e=HkH%7dC{d*)x(R;;+3W;B=#$EwoQ1rx zX2H!~8rpE0syX*FMS_!jSv%u11v+FAKpu0&xX}s1pY&v(hEt^7qtCTtR139gj;``% zraz1=sEn8Onq=Y+jkj$3H(a4ZZ7+9<;i5GUOd7EqX?$oH2mSz>hJ53M7Q&gIAd?SRUj zu|5)-T1Z4|k?LDsYz`xxTR2=CmP%1TXOSA8T1*{JNB^&u+T22Fs*qHYJO*G(ZLzr$ zAXmJO3PMpvD*2ZV12m~_JnV3PnEhnRFGWy#WLw%?a$jE(&0OPz5p4we7~x%Iqy&O2 z-(sw1wEVpha#BWherj{*Td9%|O$7QH(F&Vl5188AL=hQn#j%_M>3}Na@)s!KZ{Yz1B2WA;7+m%J z=s=NDw0{>nVgFFzAHqNZy1xRIKmnzKe;2b7SE07q-{u1hA|GYwVv#L{Oe4DSAIqf&_JqqhOyBRD0GgeJW z>d}Mg70w-=WI1#B6O4)dWAgsqjazFH+AP?Zag(of_^9kEL5uYq!xJ8u9a85e1O1Ohe7dQ;(wYS;F2nf*x6ZFY3WbgCv z*3QW}q7KSA9<$QZV>>i_X)+a-8NAn2Vmk^%531r!A><O$4g7y&ID#o{u?I%LXRFfr|5!2P;C*gy~5#uK?Mh zQv8nw0xAwOVYs-_h<28tWZBElp#?=!%ipDGssM(5Wb;7d@O>OHh>cn zQnBx>1K)XIyr?dzn?|${T|-dc1axM!*u2jPXers}q--j44dg0LNsnd>qfg+U_LGoK z&RxDCCp|hn_Q^^8o>J3A@^GB9m2m%11HXh`#d*3r;Eo^y9?vr;!`^&3WTq1!^T)b| z^*C#RJ65+Y1o6$Tx$~P%R1W6TNRQ!_#}_pW#})eG{xw6er(2LN!4h$PZ>_k~2bToEW?cHY;%1JF5^S0FTgjm8<4dXG}a!LVS-?8|t$^4S|w`n*^EiY8Q z=WX-l?a`{T5gF>|oD}iKw?em@K2og6x7AG1_*m4`Yr`?~LP|a%b{#;3E=#94A!a{XS=A8dJA24?s28 zv?{-xiMO+`>j|_Y2hMxAX|z`TJsOGDqVNx?DeM#J4k;#9PW%_oIC?T%mQoTuYUG0~ znl#KDB6g*@l{hVG$kA45=LKA@eI==;GQt`9`X1JyMzGxQceG4%gw-VoCf#njr!#Q{2j$?N>8$ z^h!T4otBJVNfky0~#rgy%I0NChjy{h!LGL=gEGx94lRTqdqy=<=l=;#GeGA8`YGz8lWHD>|;R4d(j{&FcNA#R?VtsDcH{57uNe)ZekQlhL*1W) z-#v0U8UjVHFd`<&lpL8TQXYFAdoizE;p75MwvNBFaX;2Z*oQ|1!?P;tDMYQTeM?{( zJl;FFI3 z-9H-by&e#1P6-=*xnE$1@f1NNu>r;nGZljve2AIhR)7dsUg|mmZ(ILPGvTrnFv52G zx|w|m&W|DFe4DkSd-URpQ`Q6Fb*AvEH%qG@nWa5vT8UxU*H(P<>2vBK*Qhi}@p(i? zFi%kEnHbKRshKB?51r#!JBAU_js_m^E}CpgisH2i$4x^5?R^(iW_csX9-d>Ww4pqE zDiHU?BWkUP=OAZ8j#xDA02;IyzEl?FnI`eJUEmJJYRnpm8WVz27Y*DFR`RZ#0uLbH znxQVSF57kL`FgkK;`lA!Mvn5@l5T>nUPRL0nbQTXv6T<_uH}*i_O}4ey?7~{<(Cz9 zS)9yY4!bfv+$>`;FMLep1+b(*7t`NR;eWngah2v0jdkQ)aYN6W zC^iasr9sy*3Dsxcw5dqTG=b5|R2w0>MA^DXmZhLyqYp-YH zRO(|unz^cK%xeU2?=Szyh-)^U^PcxX9UTeE=SaH?&WjT_?u0PY19kHuM>c4@Kv1m$EA?x4QO4Z zNqxjgB#ZBDht7aeV*Z}O{0qgq!4%aHDE~sHFNr#rsNB;{Ol62ya3jwBG-y)p)Ny~? z(Drb@b|GV<^05g&ZG2zSV2rL?hklCkii6_7@pWgCFcJ=<>8-mgpX*5424rShD&FP; zv?%kO|vUa5wjA7GdgP=~u z)5VcxaU^S~PV@Qfl3_6EKB_t>?{dy8=^MRBg$^Z#My^UBx<~!&-LfT|LHE0iu{y~~ zU(`yP?=^OZ4x={OL`@t5?Mt2secpE4hkV9KFJJgZvHze>gx*t{HbvxJ9a;j&B)gbB zCX|PG9V4Nc5qYX`zz(Q0PZJKIJn`A~pm-Jt5ClT8?6i3u$*0I(JP?gyUpj_7jZkzK zluS`PXBdr89_&qf^VfD-C|xOLnl^+fwv|6kd&%1EOD4%)z|@Dy?#J|Ahx2i*r{wfP zVOsLNh%Fdf95u64_fS09srF)D8nQd~W@V>bipH>c5I5+XZ)4SbbPdFbBN@9Y#4qQx zSX16a(BtiB$Q5rzlXaBHzaQ~1D)L}Q-LTbvdu>TkNCHUQjqG#IphP?;L{K)!GMcXs z&BP^|HI3uF7vdG>7kGU>4z8|8gR?nPD`?|56Lr z%l?uyyO$J#{U(>ty_QQgD^L^Mxa=J~`6sW7N$!n- z=m7-%0ku9-B`)jj8<$69C#lZB!)aPG}V1x z(`{50pbSJf@()KDS5?#&Y%YIIU}cu1B}j>`U`C=%v-{QO_)zFjk|#LQuD3`(Y7QT| zhw_e7>uL`bN(GB1vmfnbTC{}8W}i9ga_zJ$&OzGRmwOxm-IumrN0|=LEK#HvjdqK` zkRlV;!J$2~Kcl~@&7I)c&^zz6*wI@;@6e`qYZ4@5{mIA#Pj*~CzIyCT0G9$bs-;*( zzCBb`ZYykxLGj7DOY+N%ZOPN@;&ajIH_@f2 z4NTWkv}#N(#l^+LHpXA8_lR1KtS(!JM;zj}wW&14dxC#y4a!wxMPX3wMgg*`*{q z39k)m*u|lFZROiM*w00Y8*@avrjg2%L7Nv9FjnWfDC*^9ddu9oV~=)jsg@ry*iq=R z^@-&X8-vTgHf!2sAUgJz`2;)Dv@Sb!8S$M3dF8P0j5^kg{wm<99tl*S=+FwNz|8Z# z0bfR)HTD0dL5+KhM&$}9GQQ!d@)^epU}0IOLqg8f5@)ggV6-kgwYA>#sYFPws7$_% zCeG@QuM{1WVwz2KFY>6+;8#WSTTa_D$MohqUIFs(EX<8gfAIbw z)w<0Bgezu77SGks@>opvzQ?y~SLVv-hx!pN5gUVH7|Y=Eh+o&NfX7y=x@oyW8<)|j zN(CwDoqY#Gg(uH_2;MZ{X)AH1HVeVBa4pn^a5-_MhNzSP`lF-!6^&<~g@qQL0^WiE z!%lE8!dl+OtG?c-L@v{D4Rw&&c!4IQ{3xXSSJG`@1o{N)a4*s~c?c|}x;_0J_09m| z4hC={!p2r^;qv&Z`tLPA1ert=hcE)wv1VBg!?|E#MN)4LF#_+910D4?au1pET_H8F zz=lddL`rGH_n1VBdV_%&m1ZPBjMBjDzZf3I{~s8|MgI*WjC>!6p&t8Jk4Z8h2F+RS zUknM0{{_RIJ^wb<&{GfIJY5JQu=^)Xcl1fCEf?HO$*YOJWYy{TNKi$kg$ngPEq#1H zE)rbKeoX)|MqvFom*He@Z*<{X3y_Kzch>c}KeI3T0NY6aD_#*8TnU4IN*_`NN+QtI zR-;0X<&)C&Or_3QVW%n39wZF{Ei40l$+2|S13Ef5^U*9VtXUbXSy%Gq&c5iue_)ux z!#$&b*T(>6B{B@Uqk=G9pwg36t?AlxRG@y{Q72{T_GQ(kF)X^uu)Bn?shNvQx55`d zKpm^9P|y_MQn%!oq|W%@x}Ql1Qf8ZjY`QBbrr!^Fa{prf;JXq*%=LMTni}fvv!OT# zFC3tn)Aj8jH}xNqaL)^rZqq8TRXa`#?I>7@V=F|rP@xrSb(R$t>u{N2hV>BCrRu6a z;iJ1(9U4RDCJO^$m%?r{%~fOnrDd;a9qzh5m>f}b?gU|Td$%DPv36E}L{xj%c5Gi; zcvFC8oO*jqQ&IfODZv@-tyP*;dZpDvghQ|CgmFQh^JGiqD_sI3`QbPNk3&-^KZsZp zHZRxvMiok8bT>`eXb*lMh<=g zYdFjV)&BDh{0i?@X_pGmMzOcDeys{&JY1YB#D3WLZAC7k;EZ*qf(_xZTEudrvtI)c z`tW%jtXw6tO4;vz{N|1PWysmMNJ+Q#L$3fG{j+K|()SbSV(O_R+u?&})%SwcWAXPi zn79J4bfe^P88khkOj~qMkWiaj50u)+vagm}sOS~pZqu&(O}*3!ho@*KZDo_zkK4vlGP=hccW-w;p95O6QJw9}#jv=(O6g`YctT9x1oa zsxz-uM;ud^wh42KckUTQn~qXd4I5@ARarvEY`b z^L~@JE$rMSyq_5ESZRlP8w#p*&*mbQK;Tlo;}`D)Rh73cX&xA$Q?Lto^*V{u4z=!+ zEQ)aNZE0i^E!bl9QMlg2fMox4@YWW0hV}k4&=1kBUgytR8HP<7VmH4F?m2mvfakYa zd}_Y7%25JV7<9sQeN2j+;-J)0tFy8Cn{Y;}C)MxB%cR8=a1&bA3+T%xu{n}q;o!9N z)mV=7G=BMoSIGQ!pA5Oje*-34-JmAByI$e5GXVB-JWp+ps8xb&NvzGj%j{v<(z=^=%Ya1y>iSc2ExIwBf&+HH=_ck>Ld3fkLo5d)QmNzU* zB+FpDs-MjT`CIuc7kN<+i&Cq$Q5elAt_^-G|KjSi&qUA7Lhcb3IXrY|9E5?--m4iF z>EB{B6C)PQku zg|Fc_|Cj2hxbe(3GSYGML(}n}Wc6QM?F!U@WRyTMx7bFUCXuj69DN{auXgRxaBdzy znGv|CM8iO=gJZrj-AXQLSmY|lpg#F9IGwuXObqq5mZK;?KO;~5Q8DtOrX$%nc~~SJ z_lbxwhoGa1mI1ef1UGphJ$LOlaTnt249%4 z{UbzY+D&p*k)itB;`%buvfjwr5h!2`FIyIFg6MSlBic5P2Age3S|}>bBHsvOf+J2# zgLNW*ab&Qq78y9O@xC#c4OX$j;WyN!5#)w`GRDzmt!)^O7zL|_QTp{p!B-n9iDo$0 zqPO(Z1>XA$yOqsHZ6pJCDFM@|MRVcrIZ@Ch*05}sxqTzKeLIZ??!<}G{_ns$G`ARd zhyGLn-l3AEo6$ye3AV#VY&u&uHuw*t`^H1c>@~~5qw}=`ZVjt+{te}0$rklxtBMI) z_|;edCu#6Z4HYMwi=`&YAl2$M<~bWcZmBdajUu1`KcKjDHe5*sU@OT|I?8FpQpz26 zZ_+THMw~HRaxD{HYGF{Lm{4KdG&bSsYLi)ItRoJ{(^-ftT`yG{C@xi2IvLMBL3uY^ zJNGwl`U{Aj00fK_RsjH?E2X8JWY~axTo!Bqr^^+kZJ;Ab(I6vpX=%uSIG`}GV(3aj zeXKRSRJ^SeAJDQO8L9{4 z;5blL954hN4Y^sgT!;0SO8-?d&)70Zvvki=qey+hNUb4{<`K=Ex%322U!bm-n{aC? zxV-pB6Kml{sVLyDLrZ}U1?r1kQ2f`SDt{fSqN7)pgs>Kpj?>j&TuOm~3rNvyY}VMc z4jqdHXf7VNmX1_fNB}O5hYVCY8Wh>%4&IH})3Wx|s*5@Gs0I73%xlkWl$qQ4C-1$;1 zmM(|%ihyC zSSxu2r;6=x-+%p}O*3s(PIHFKpLN8#w&bor*|d}@?Z45?h56lmEHE_ICo; z0p5^@dXJ9+e~o>PHunqV$?U|IKU=c0E5w6pptmkDZpT%XBtU~X^?*}xTTq17aXplK z6Pf!q8T0#?y%wwwO?-V2h*@L1UZq0ZT=N`tt7@Pim?%In8bkvEi%bvz{S|sH!C$z+ zQO-99AyVEMG>Btzoa>yv0=ED{6nyxZj)+@=SY6Oal;L#3WIO)Qn`YfDD7yI7aN#9e zk`w)VVOcrRgu^>rw&Fx=bco+6296U{1jp&KTd#)gAWK4!IFKwm21sW1mkj4GnK+P4 zI1gd6%LODNy@B=M3)mtRJsXbTgK5qT7fg>ZDXYDPO`)i~&gmkLzxc+|;B=69Yl#nw zjuV7=`l%^ML}^*n-n3-VOK^brhEU!B+(&fX#)I*<=QZbl}nhJv#& z%MKmEO=U0(!PXie(3@93ZT58~D?r?o>I-Z{_^@$Zpumnzhlts{=WkL^v-~s|syTpa z^g)XkB4A8o?Unst%aeA5lr|)uzre+~$i$M~_mX?j1P0j($})Qq3%-9E^?)$X-Aa+82(|x( z$45LZ!C`kDHfmNlPv6KJ11BQ`iH?nhB?v+&pInq*eenVp8!d1`c-2m`VH3#j_ToLaj?A949?SO+uXG zLa}oEFalCrJZ6d!s1lyuldFTXk^v3R0>f|bN5sv;{hjg z=+#-vIk0*;-$(=){a1}CyU{;tl*qYsGqQ6*W@?6P08Ws;oqbivH5G)e07h(iC#_sTw{oq4 z1iu^;iq)1d)hAByC~$K&%Ib%+1Iwn|p*O~vn18bRWqhr}u%YbbZu!z9ACOW@$AfM{ zbd?|-foYvhGRoYrB_FubY+o~KThteht15G~*o=5qg|Q6HoY&i^Y>>oFzF2o^S#INm zkTPYfG+rEOsSpO(8jVv3J6<0(EE*_}STnDYXXvh(Gbp4RlMloG>RDh(5z6r$c|Up8 zwrIdgyG4D!Y0h8}zKlHBavkbH4fd=)Sy3M-C6j@1y0T!b+Qc0+e#l90qB7J_G+-ew zliD0JAs+@jR~dLVv9^~<(SWQ*Vg?2)(u=ej9QwbN4*QqV13;y9!5XE6%K@-G(Xg<-&?0Y*C$U}r#owouQmg@_jY7>=uJjzf%^QO zR%_L*Gy_Lhj4XKvwhtzR0la{X>(#5P&-$zXL+gZr|E_hZt-)l`fTN8;S>yneYr zKJ5Oi)vZh`L*(E#4TH6GcXiI7X&AwxXrOj%;k-V%;pZ-g*IIUj|Og!f(4(;!@6 z{2UhR@Zk@!*J0lq7XIehn?J#}JEtpG^VE2cXDFEWDQM?;rceJbzZOoWv7ow8;QI+& zR(aj%^!2h*)#i`DAV1#77#Nk!HZYX%JtP?ICgs|mqibk>U`%JDJmVhc7V}<8HG^Y2 zt*>VdG(ZmJF~#I@=-&B-?x>~nIFqp4LXE9ZxFW_I*{TuuQxD_cTrw61?H=-S? z`Z;Zb_Z=t#czJ#(DO5?x ztc5k9o-L~==x9v)!Q?95F`pAH4uq!!;0NFy-1Bzik5wu)2hc4QefrqK+>;XSgaqHx z#jqq@*P$H>~1z&GBkE<8H#YJwZyEE9yC~G)`?i^bsQN7W%>OPn;MAUv0|;T6@6% z;6BkAqmtzGe|;IGe@b@fQ>AQAF7oPt(496;L}pwzPT<*wNYG{;h;A?{DGqA7)PMS?*|;4u@Wc(wUHKxGf<;O9mW!`6r>BdDZ} zM0;&(pum+->dE%VQ21BUU47|*b+T;kGCnvP>6(IX8(I7P;0gh2)0D%_*DcKQq2MEr z37B?j=~Fmd010m#R-_rdU7O9uT!9M%<`-JAA6SBE&^Zt?w8>XukQ46@Oj+oyp;!W= z>3DCiY^Mf(g_(yupV=Cj44s|M>1cv0+2(#Gs6ug>+9bY(gA+REbogIMXXB=n{pyg77Eepx%oa`saSTqM}|u<2?(zuY^)#Lh$hjJjy5l(@oX#ggtCG z7`^#x@h^3liOoB_f8fo*_m81BXU3aUGNn#F>t^|vxq-X!1zkbn1YEaXLDs&0+uK^q zkom5e_RUb?$6N=^V8v6B2g5h-oH9%#0+~D$Apte*sr90fmVBTohSS&pf2q?%x8p=d zhaDjW(#K8XqzD_{I96_vOfTL2<7=T**cGk3-4g{{+RKWWoBN_A+;P3v!##z*&UR}~ zP3*G7-^;w-NV}EP26mNwo&EbpD^|b9n&<3qs!^$ieVkE>dHU()!JL8|z+e-CKofBJ z0t#3{6d3BUr3xp1z}B@}v!-nQVsHx7YOta)RxYWNDY(0VWY~pF|9?1p%cwfTCQB58 zI|K=CfuOFL;AZln zquWI}#IznPteTpQsD;UHuuhiFME%X4!NFreHCSAkD<0^zgWEarJa_(^nJ|tne_7n;U>;DJQl|Qo-DU0^Oz&k-Yr4d&LW*PlRU(I)*gQM z>PK5fsriN-@wWr%A^XU_j7mQ1+4gNAs_~2%L-bHqvz`|Djk8$I*xk^SA;K#Tnz6nE zxO5Sl?4P)_nKwl_I+)Y8*^IaZQS7s_1ep_}tmD`*aD$b{C2%5^_!IRZyZ-c+FUS7B zl1}SG+Dv{UtdU%Q+V5f9tLM>Ly(%q=I_?H7tt5l^Hw|nQ`tIn=;IL<`X!_(yDzFzQ z{5F)ae;QQNoXKSAniDR@?lh}^3m#y9Ba7%+F@A9|ezS13I1Jyt_hK`7AC_lvsVa zv=bqDj-EdeTCdzA47c!(aV%8-sPf{0Ik{w0@DepJ4^gE{OMw=fqq@s-uHhz~lNpY~ z<)hsu-5S^$s3y2!2*;JV~>J9s&h$)m*OyAdYa}+ z;m`uIdWezCI$f`(ZQWx#@9A!PVBJ3(paOwUm7f6{lfn@cs(B&b!iq4}p^O=Yy(%dn zy*wFTMxG%2E2^{`{+GZ|^(UkPqL7ppS6;05clM41Ug{P3k7l}W3A{g*QK`76#*DTO zN567Qzrnechp)Jwm9O~i;0b>Uai-(t%w$)P^c`eCFqnFLS`m50%-9}V6*Ss2MHb+g z9#E&*Y=|h}h+C8VJZ_9GTo;~U9)8LytEoO=NUmbd2v!UsL)W%uNT10tN}Eer+e8Lz8Yu%wUQv&PNJ zYbRz!W~I)Wu0kmuU^k?KxhQqAMm>G)*hb!>mbf$NCvJqDxESQ3}l} z#{MOg=&|;b1vp2c(PZ>dImh(m>^%a8##wyw%E)kk1EO1F|-FIah2){K}_P2DIv?sKE zDxwuSGG4=(=r`shS!)8MH9IMBPt2kh80H&fi)<(J7QRul znG61Euhw($2ef*CpPp)4o>61^es*d z{Ap|zB~M@Dd~|%NG!tXo1Ni|H`Fsrx!u$m~_?cw5Pv%nu>lCf8uE`cuG6(PlyA$yc z!hdvw5;c#}=LIJkwRKL2tRN-Yf3`qmPwqOOp@EkjI-^G_A1tq~tyFdnP2giZS*+P% zf1@#k@Hwip9t|HEF(_+3)2R*SZ!UQ}(zTYIrE`mP2_12r>;C`=b5tn&{%5-73wZuT zFeQ1D9!4ePZ}^5nSg{k!M14Jje5o{H@*oe^06CMfS5kS+Ts|eR0_ZqpK8i0&KV^<{ z3$uhN1>$SPir5PovL_ZEh2KpqI|e9kK5F~d;x{MoStC_6kZT60yyHXY`|Gr-ZCP>r zJ6&ScF;3?r6#1s|G2TiLEy_wE)CGQW>=XX9AVOF*n11EElQK zPhu!9X;`}N!=0sD4biZXkKz8QW?(Z?Q#Uk`pNs)+B`=v%s1M)Bb~|s-!^1oY8>XF) zap=<0Vya51?>wK6AwH)d@7NTi;WT=oF(*LYyjzLcA>)j#YM3bcGZO1fA9aGH#7_f! zm@*dwv>c<+NiM&ItW>z0kJ_Sav7bLbc@^dj<&Q_C#@@SOcnfJ)2Dc4HV*{aHq{a&g zf?=s~M-cB9?{oadN?>NOxnjOln6ah32c}n!9cnB>Vm34?O57x)~yO)5qsHu6!#1;AiX~g)?=k+uDzSFN0a7!jnz~gIi zhB0L?19xE~<=GbmuH`rGMo`x-EPVoz%0 zKF5$XIaHA#j231RugLckm=%(X1W}IE;AqY+_%8yl&z%@dF9pdhI)s1zhxfc73*A9A z=Vr*vQ#rvHwAXE9^zT|6N7Ox&CF2>|~-Tl;Ehn8qV5>%iFo$!?M_SS?&XP=Py^+OXx|XrgB@8op(=Z7n#a0srXi0W9aYRhI&H;t?s0G2lJ>PIK|C0T z5v_`Ky5X6D{5`UGBDO_z+KD-9eY2*C6Ag$l4a7)aJ=%0r&hfz@oQW<*pZ6FxD_1NZ z5}A!DI5t7>O@$re;M~YCTY0;F`w*Tz7+NbZ;@nQpMOI$<-kC}L7(TVS8~(3>5M?R`MbVBtBy7oIJ0JRHs(!*}|N z0Ev{x>Rs2C6Z*%wzRM^>%@}2EtOFg!O2vs`L1Y#N3+e!Q|fDj z&B=K2;~B*Jj^32Pdc$*1x+bgjTfaUplIV%ZY(zqk5tFb;#W!i|N-HybS718Kim7j9 zaU(-*`EA<&V_@{J2sQSMwRNebvHNjH>cE(!pYRJ>ME--ay?3{N5P4ND*8g#X3$8G2 zXl#Jy;Vbf0``%XQMbEtEE45xCJ;*v9-J} zAV?ehqxdyv!aHrcxqh449j(4X*O0t@S(0j()(JO#iA}yHUO2nv4|`MSRyl6T&!C;@ zK|_n8w2w6Oq4` zB4a6?NRzV`DFh3&&X}WOtBtC*@WVt|Fe_@EBER;7erA zEy!j$tQdtx*S_{+e(YIE)>B~!hWF3&YnHZ+Ku$cWvbRpZPK+EC*G_n)ucvT^9kIA> z$u9qhggTj-GeKax*od~z#c%7@4Hht*Q7Kg*tmLK; zaEboeCN;3=0%(%;kz;S4#n~FUs;a&8)@Z{vpli2;&=$m=jL{8@ zMmq#J+K-5*ww}9Cea2Hpynb%Uf@8ws@L`TR^3+L}u<6=h&9fNTI*PH=}UUlD7mY>=@-CSoym}0kRXQ6Sb~(cL>02@74|v7Y9%8% zOb=7V!k-PYR z>`<{f8ZQ>jz?({X&Q(gAoF1ZXH+N!MM5Z zknbxEn)IN(NKW0iLUqLjsPsm0e;i-p?jpIqei>}zF3bRJ8Uo*26~oCj8-@4NHoDeJ zeC+5Cm6hMJ?-scW>4|ieue&f8v~q#n*&X?jh7ozC$eBxo?0XieiqL+H_cF zfv5*W5M8ACbyedHQQ9C+^*-{b1ah{S$y4C{lWwxj_H}6AM_jfrhPLH)-EPsw4L_$uk`WOw;TSn0NDzCR(QTL`3}~p` zULL#mFa>mU>RWn(VTNNC?^=0Z%o`% zAfIBgT!d8CQlQhRmP~e5LQu6@A%LKq4#BgR(DTn)SHKuxclcRZ>z23nr)2V-p+XUk z>u(Fo`Xu(YSIuPi80OvY?D3h5BRKo)HwGxQJLrV|7mzT1-bzVl!^D0zcl(w#ZFxq= z7av~DUkgmHz)|lkA4iqxWvBWFw!2I!D~X zfYtGFK=biSi7mLf-M^(%eUYw6N}hw1Mdc*K$ipYYg|JSeK>bHexI6;m$h&IU7Rz=8 zOO+={e`tT3>T_E53dN2}L9eRQisC%7meg&^`#ze^q63=WeV0yG`zvV_ZMH3`+P+vD zUD`g`YEqI4JL)I<7s_d*Zt|jG&yqRObd|=oNo=1_Hl6uW9r_$2Wk6j{=7;7ma}hWd ztZ_N1G;^^Yx=s;uG6C=4#K%5f(9SisWCXfMGc7H}^ArQU2L$K<>P&B5gn`1;>k+Bv ziyfCyVt?c}PFXNN?e^mCF^e|_>iT+2uE=h3fhYQu(g>L3NEumxI3Vl>t_yZIo~!KZ zCLdP&x}EfT%&3QRzjU)&ij(t*T(BN!9F;iEw4BELxm7X%nKV7!euGia6SCiZreeUc zUDU3+*|yuZE|V7SF`}3=*(nWttfDs;Ib0bNgRmY@zyLCyrVQx^4Dvu~dRzDf_!cNh z0S4s5AuEW#7~o*sFT)-Xp%6~Xq@2ydj&zQ2nAR#wPD|RC01c)T8Fxi0`f_1&l<;tI zD+}^i*azd~XkdxzszD(yN%1`Vg8#rW`xh3%zp=P!PyZlKKhLn5HKz&@TxS^*|b>nQZh#Kx9-rTnTzEa$K-y}#B(JNp`&+_b21Gj0hhtE^jEV@XBG%?9=7ihfLnn#>2lrwZY)l^(}?6uLwO#IQOVcQ@U>3EAJ<> zGVLX*7lrEZV%MIIQ1DGc>z8X#qYp9>$vAdnFI!0he`60eeJOv#rz@VW+!B4}U5E7= zWNxpIvbJ*BI6a(jB%IXNa}t~+#O?gW3{$p+Bb9?J_+-MIJr1nAHZ)b^bptn5&>~@O znkDBIwCCDjvG3w+s1LHTv|Z}eZd4{Occx^#qjlj?b`lvQBW@0JXe(VI6fXx8c`<5> z5Y#%mk9N9%Y~Cpca_V^%s7prQ1hh8(iL!Mw#oAmt>twA5it%hQl)hfg+rd$Q+`fvg z)E0!;)Z<{m>}4I(!x)ytw$u>Z(vIi#hGZlt`eA<~mul(S8l1QsE&? zx`h%Ixq7zQhMoLt9YZPqwG>2w!w>Dj$Ux9Pbb2@E(IkSD#wcL@Tzv_68?zC@W zd^U@BMSgr>yvsf9UEnejzk9U!u9x(Z+KB@&=G+_I8`N!$bV8(V03jVR7XBJtRI3es zNq#>BYDA3u^Iq_g)9Q!Y5%JWiOo~vO`ix@ThpR|R<{lYE z<`et@IL(EFQBU{~8wI*d#|Bv`{ozl(X-=oyGuiweYY2;@Eas7G9ErHS<4%gVWeD(z zX%T#1Ol^ zV;0*Nnzrbq1CECw&+{AcmP>OB820U%=f@{7((y&fSjyw>O2;wqdb1G7k3k5M?j4l| zX$Hugw+a|sM0_Z68byuUUDG`u`<5Susc{Z6%heCfv&PdnDGk%=g%05Hl15hIzQvBx zaStmZK07ECFYl*X{h?_kNr;B!pj5p_SS0PolT)zQ8Bs%<(^B>($BgiyDvaT9l_{EJl0cW-XNp%oH$aIzhX#0I$G}E@3?vc`e{4U2m9QtRqDmMgrT2VoD-JY4A1seW+g-fX^jI>+DiQ8c(jt zV~u5!GKktMJ*2!pJ0R#Fw$UXg?SL+Oc)q;Tym*fKbk{E35k12ls8I_G(MQ^JLGSn~ zi*#7IcKLGR@m52!8mpj4qdPa4&w+k%=&EK1hV>z$pwLyca*xX>c}<#>f!q#WDJ7k=>6T&j`}G-8uM<=jN@?;mK2da(cm{f3h_ zA9$#0mw|D9LKO{@d^*c_pE+!k4(-04ROWSXIupR!t^C^KTRPHUWt6)2KxO^(><#}u zEXiQyy4A1+R~qjqb-bP+W<5t2L0?a$HoYTqYEp6<`M5?`X=RAI?RyPYeQ^fJU}aLs z>VWsut2LvE@%RaqNmf*Ttk7RNCO|sHV@Cy@L{aKlo3Yz^3stG?(Bl$gQfMZCO-zNp z%BI1@DD_!s5W@wD!?B&t!)RKpdIpnw^m>^Z*jKn+$uWj^=u>!}RnD@>9>8ql7= z;ie-t7CNCK_D+|{7BvvkU~!2O1M(S}J-Zgqj3ik@7AgEo3fcTGDdHmIVnkwTdn0%$ z0#$j>$JFKhstZ**?9YpfYZ{yDY=*CXVnY<^N#7L|?Pdt~ECn2{QOdfbc!oa9O&Zo9 ziOp45a~6f+;Pb=c0M1Mi9)}TObN6M#c`IxF9vr~kmIj0;(tEw$2Ky7U0~nORYr=^tu3%jJ2Gi^c*%O-!sh;r5Bs+g^USeWP;IUlU=bC z3A6o^folh<(9(EQDp$DRp|MbPpDl%%&tI%6Rf?3}O%6Q{jL8k4WA5gmN#C_8^O7o3 z6h{HL`Bm`%ElTtX_dAqRUmFaqCWkDA*$0(<}6}vdG}KI`x8N z?Z=_qrZd$dMbbvj*aT|O(+XP;LSvjM>M@30vq7z#p1NbN_Dj!Oi(qwtOJrI1Cc>3c{RJyPesHpOMTn1CZ6gC6)3am$rM;c&11!Y9ijjM6de6gRn} zOZy>%#r-Y>lR6zZ4#wb7GmgaK@jK_(9uf90jT|Epqy)E}#Oc|o3_mRHVJ^Y~C`oYr z0#i@7F$xL|k;^iAQV2&6R>zTc<<{z=1wzZNg}GZ8E|Pw(--s)JKH=DY0G1_9I{?Tt z%)s51Aup%QOWe~A#v_r{LsJ>aJ1($uVr6x3Ft#Tdxsyc7w6mkc^1c+F!%9wm03}!s zn`J?x49jE%gGOO6le^#SzFH0ryflJq0EC`G!DiUaq*ki(ru07Q!_X54rt~$U1xFPV zq{cwG8YYiKa>wNCuTRBmf_Zfw`MapX`2~s~9)I6RU|^4ZNT=a31*qU*Yr|mszGTXr zmWE?;AQe(MVf3kl<~GdO(5ITvouj2*OxLNyl)gF=!k2e|&Zqwwv`_!1b1F6J;`^})oothXsK=KmiN^Km_nIngZ zmWaN{L*4z}5R)F3ciY`6V2Yx4TH<>TS()S#>CX&iS>op$n>-<*x1Iy0uXm16vk=6h z0%oZVHVsx|Lk&CW$6QRlM4^WVPQ`yBQ)ftldiNgGn_JVnvj7}lT;e<@1fK=9fX)%w z&GCKKM-R}T315k&2Gr%*V5Rcq^aB?f%+l7c@kc`~w%RFn7`~sg;chs*njy9<8${*M z?y(vAXKm-dQIXq9>HdFIO^sHN{!f@%*(8EmN;!Tn@w>m%uf>-g;7n%V-;^Z>`Y00U zqcz!o`lu4FdGehQ@1!m+dkW+g@ zFm99>Uk`%1@jW7_@3gpXH+vlHT(ChrP_S^_+ur&$5JZz zo-}+dYCM<^;aWP2A{C423oUF=AH(zY7rBpRPjFbGWKj=6G=Q7ojXsq+mQZc#^in9{ zXG9KB4?&!|UgPS^t)IogeTjdTubJ!~10-1_{;xfu%v3 ze`T?P{Zm$Lwyg;N^Hc7mq~r}Rd5T0JFE-o&Y+wU(NU&)4_ zJK@llpE=)?pf7RGH54l|VY$ zA3fJKmI8PQZTAm%oVfN<_$$bwl)!PWI&0O4d}Ct8Q(>c`PpQ&2AUqa<-~13)f^ z#cD6)O9!ow1eP~g?LX{)=*O3P2~M!^D3RRsJ*-f#~todgt|85~?)c>T)>ml1sq)s6ZaN>e`hqAdLQxM;YI$dp`$1 zXpEm?&2AXDo?FN?jx5ao-ZYIG&@^J;U(=Je|1|xI@Nd(X8UK@MB)kva>X@9%?L!KkTJT%m%>jm& zdq!|Bm$r}@MdtS7i&ZS7E#r!JHtFjOd@@X; zbJk;Yop!-6ZVS{FYjntJ+B8#mH2PCNnK9zgk}1!N8C+#LcXe9pR^QQrc>*}<2C&`K zLaE!n{;XPdNf2`P9howzht8%FsV{$ZS#q5zir07=RN-=li!~nttGlYWHu@kxKX7AcCtFinPSoAd%nD{ob|~MLMDExZc#)n?RE^*URdnS*qzKH3ShCoI_I( ztHyuhd5v3hH7o+NrH@y4CDG7qvMv2}|6N%9^yoWN#y%(g&t~#Shq6M9&f47HHrw>V zjf8Mr@FxE4KW-ly!7%XE;o$y@Ny8^X%=wt#6|J@~AthRrWx=+UY2`qR>u6`VJgHHK z&(A92{0>HYe!-&2oc^KC!@X3~JL$o!;)#!kSBb1kV<{32h_Wq83UHgL7`jbYsdH{X zs$3KJIVAixHcxmpSKitt7jeO=`2);ww(;bhlWW>;Ih2LmG88lTwu?q;IS@;mqNhmJt#xddO#{3(Gne^YUBbL*JT06< z4JEp?kJYb1G2S=6hQRT|Bhg{g6GEx=ts3S&*x2&o(6UTB!f{A3blWtS=+2N;+&Ib( zk_01DKC9~(sHTA>hQ+>jdaR&DBr+5_5ZKM_$}jsulV@nAbc(czV=)}WY90NhuMYV* zRVq{RQ}QgrF&xk~tMLWdXZ}u`SOJ!%hK$gcNI{$p%J6;ON}c>*_Ms0EW71O8N!xCs z;G9rHIB*dscDRai-~qe8(a=ae3D;dic2|lR;fG!WcM+}8`|JslZ2#D;<#p_=@S#c? zvu&XCO!iz4o=kDz4k`HtY(34zpG+Ac6~0c|X|+xM+II5=ei&fDc2RJHfE~lxpsF=& zZBPb3#7Vrw&bFaet{b|Ws^E=`+f<~)y9{>R$%wm1BVo$6L4fOOR)HDwTIv-TvWs|* z?z&D_s}!<}hjNCCYkO9q0DP>IvS!U*@}->QUo7#2Fk>E}hVWJ>Lw0#6H*>d}1w99T z#{VaA8G7^EZdPTjLA$@1dL$nBS});;8drgP9WIf8)Rf_e{+>zS-!pk#PE(`oyN-dr z_!i@9CA(@{az3PK1!WWWw)o!E_i*D5yVjQy?i)A;iR8$X@^f8(Kgn|HIfZ?n37PL%y)GfNwU{s`>Gmc_Dsp@n5`^ClQN7L18TU0x2a6_=w>xX+dSi56Z=<=pU(FyX$ zFN$ELm76lFZ*?~k`(EsQ`(@p(CHvQJ4F^# zwFkl+%coNZkbuz;{*vii0ADwjN#2wb!>zg%Wd{$FASzps z;yPRay+&-YEIlAGq()?Q$1SH5hMk0zh`!&g<&=t#M;QG3!#AfRlHjjrVf+D*nF+2BjuM-vV*M+%8G;pZ-couT-rJ0 zm*J6;%+So=U~GTIRB;S%yx@^qbmKIuo;-SsAgWFodx@l~68h1G@UQ?OwKQDL}-dTgF#-pN~;a{X5rhM5hAEFkff`f)Ei6vay^v&J# z%RNzB+Jbi|6G)wOriIj~h_{G8|4%0#az?G{W||Crp>XFC6Sj|F*zA;I7NMPK{0T~K zM2QJDdh zi&)U|I#JsZ>{v3iVj;M7H_Mpj>CE*?a!cQ^5B%q~9A9kSXd4>xkBUt6mF3+Dirpg; zdub8%l$i{J*4vNQAy2p9LS+@w=Bvf0Desb7jBRMxJ7IZHhP($BK2)>vjO+u-g7{i1 z7hl$KJrHU1v|YONd`Kyq8i>l50*v`f_S%Y?jF-GH1_lAL#aG}ZVLgnG^YnsB5;zIF zdn}mM&6P0!6j3OsTqj3wN_?w|;#dJl&X%z~1)5$1 zt>CL3-{p716%KTi%oE_YkZa%f{9Qqd-mm^F1Q-tHn}lu9J}_u5WU}eE=PT?Fd0yUSQC&$y8Ku^3;mD8J zE9SRW-k|GmSw%BZBJ{T#ze8eU51HFWLm3o~`- z5&wGfjtS8O;G!+IUJSh_TGWAwJ9FK7bM5&!vPYq8bat;4GZ@u47kc8& zdbm^#;F*OT!G4Xu-8rq+35HkXpz;aGMz`ENNEljuas9P7B~axxnggk&p1_+q$M6y0 zsigqB{NUzLNXUi{HLR#vawnFnRZ$`5&g<{l=I`AX?!h@j>~#VLq9J*oR8Be!4wZD- zn&W+jwG5p=>S+pGN{VyBKl3TS6uM18qkbN#TSRqYA-cBzmYuEH$1usV^u!;i~JAhCb8Q%t0450d*vuVv#>P!ab2Pr{P7>b zV7KN~l3@KgrSNlR|BgYMfL#tORNkHddG^1%7>Wp#hS9v~{!UlYi-SwVGJODqNLX`E_=3UAu(Jw7|#eV&1*B zYmJXBV*S;Hw%O^&18VpfIR@AluE8gh@6jpZKLd|Yz3sy-2vkR!Xjj)SH)V#V`Yg3f zzkMDrHl15ya{oEt8ZHSy)AuI>o3rvi{F3BGWI9_34L14XK88ahrCcK4E4D)HINj+2 z1>k%v`IQ+Ei4b1-%l0D@{b5IBd2)Q$cRs5n$XDxz`?-#{A3-{ovVs>%NcG+nl)UVN zgz!8_rl2gjb(Se_K6~Rg!CM`ia?VlJ_wOj)XVmsJ` z^?X`jM)-b~eo|l#2v^}u+JPUy$3yhmy;8Z$1)%K)7$UZ?WV{ z{KbjE44xtW?#BMBq21gc z(j=dj5D(DiHD;k)szjQBASUzM~tM@}lwby&QE*N-2K zUvC9Fo+tsttpK91E-wm_=1s_^4Ku^}nqK`!w#}~ZD2VqC%&oDX(6T9RH7`|Ej8^EM z6?B#DUDK{3_`CzOb&bGvBzGyh$G+4WswNL4uqu(w-r z?tJ*TJim&eLieXz&w5}U;<%gLgN=}ETA+}UGp;$H%!Qj$FKh7dGo8wW@Z0I(cyG5- z-#kmWntf(y#sdWpK;7-0gwpYGlP}Kdpvaa4AGQwA-v%gcEH51`_*82@_3+Oxq9Z!)(gcI9TFl?a}Q9nhn?EB-xz)-P;=m4&0D{_pmKm2 z4)!3xe~_-dBL|Ys|NfVB(U<>BT4i_pKa)N$IVzB@nbb{0TQ^`g9}iG;8Z8RocH7Oc z9f!M2i>}g8!7SI!$=Ah`(u@#ch0A3~ZQh9Xz@8}A3q4(rP)PMwUy(cH#z5CVu9MhV z2eu)O9Qu;*zFNC#N=aFAn7j-n=Dh}S2r8Ul)qM3MV)#H1kZ3{s<&hUEcfT=5<0qm% za|bQo39{fIFgeO;fZJnGD%Taeh)8+S(*yj}1*&jfl_EWAAq}$K6plSR-9A@b+eT?c z%rV(aRCZ<+$H9|$nl~=*0QKHe_%0b0*6_gAk7Ns$kdr$J=6ZRhl0wX=g4UuwEfV91 z7|(RI@>*j73`u>BA8hE7{FDu#O&Z9oCb^kV4iym2pHMI1OokLCue^5DMdLg%MFtoS z)C0ANN$`8WasPrQpWGoT)Ytw4wCFD=^}j&bgJbjEqZMn>3RNMus*rXt*b{I0E?#mk z`UQD^nNaULk>SS_B`y!uDvb-o6tN}O#rv-jj`S-`3@T*ltXKnq6VU=|m71JVBa5V_ z?PXCVnSY~<8Bj>!&;w`Kfwp4s+h~?ptB9~89sPFR$u_$~J?>0#Xd%NMQW!nkRiiW^ z8dAWOR3G89Q8qOw3)z(P_bSP{ENUeg*z+utIi`Ek8AZl>WU5s%6da;X?qzavqD&6B zi=B-vDqR&(m;?(e8g5gZkuAiMWM9vUH>5Cru%||^k29o*DXKIgWJO@ERjRK=8+X;f zl601c=us=zhtt}|p^6!XTrEJR0E?2J!*T{f;+VlkvI->5iQXAJAE9l|?Hc(L_4W(- zBS27~zuj5RPpK(p83hth`Q2Z3nW163ZTi*7uU&}EbhFwV%OKW<%|rfDfUEdO;0&#F zo2Gv)7?bJO6o~2HlzfL4I|AFzq$mnyJ{W*fG^1>A{}iXt189o$*tc|=c?5>(K- zR1(|`TQk_i4{DZjj-tB{Akd;rZbqB~I%UGG&AX3L-iwfn2uv8GxCiTleyyUj5cMmm zi_b?2|F#3UWxh`ahF=B_-*`%jvSogDm;#C~#mid)g7kR1>*JFf?ghyeDSr3M9;_4$ zHZWj@aV0d^sQ!2C3d&YlW#N(F9k-^fUrAC9yj$T{y14}>A-@uK?$G?>WKA-zHK)i7 z8~!%VCs?-tJ<{REW_z$oFhF3hP^Yt6Tu@WYiu#9}IjJzgSK5Ar?%7dUNG^o0bmJfL zK#W>O8Avy}Ec?taTx(dNoV$KMZm_PRhGwTJo8-`KK%Du2ole74z)mN-V0q3jTuDi2 z-eT>@tRS*fyjWLaq&I$E{xIL4rYg(9d#+KW2U>t z(X%Lz-}F)9JD9GRc@*FsQ3Bzs7=L&baFoH%V3onfCarf1Y1903AN3ab9{u{G-)9YZ zg1(5&JV+ELkjzJ&6eKVwKr?7&KJpOc05zpoeY=PUp>FlK5b3sO;md z&Jk!)&wc>`)euf#NV2n{g3@`yxB{#|>NhMdSpjg*i8!k48(5jd7`x4o7B$4oe1K0X zG4sB8hrFL>8U@7-YrTHj7mgJMuuLE{HW!9kceYwzqDOuN)pYz({-P+}g!f`e)XE=&}tf)jQt zbT-fBX_DwxEUoG_V;+z@T-aA|unV#D8bU28DVTV1&($f`imyCUxSiNn43D?XrhIA} z*887+e8)XUfrHUr-*CHcM`6`M->%R(eE{a&fqWh?X@Pu1cK^+%&v_0#ogn6+63s@@ ztz!&w=f7h_y`m}2f+Tru-U_XIk9Gcw1|9dD`(G*7Vv(;q+`yFU{XOHoc<#}~kIk7Q z8s+17_Ru<6zk{0M1L4?(9(6QIil!JU8kTH`$A1wsV2Dw0u|fXEKlPI zjAg<{t`(`Fu{SdgsJrsZibpg<0;jO>Ea~h%GnTLjJOJn5o2(GF`{!H zFh<FKG z+`d#aP!b)P;nKlT`-=MF%Q3`$Fm*Rr90{$kFSG&`59-gce~WenR>Y^-%DYrcY=Gfa zgH3xagz$Q_;fh@MMl@8ywN^gE^K%H8jTf*k4vR-d8l<<*W`q%j6FLrNw;N~9XY)63 z!&%H3CS^q|m}F+%0G7_zfx+43QcuVj9HGAki(z8k-R|urelFcszJd<}ec7SL!$s?Fy`_hn|#1yveMy z9V_(Mc$Lf1vlNwLO5SeWy)mms{hH`OH`Jp4fp${K&MyyJPb-O|acua9|MEuRr;`D( z?#a6rhwF`iN)q$9B4RQ~zvxZftxvKY3h*&eI3NBYZnTcD#$4y{Z-CZ>5yBa+D=&C*p`s0$AWXBJ-*fw&D0De%+ zMR1&)V9Nk{)MpLUaZqe1Gu+%|>SEvz7+_0vSeK-&d)VCaNh$^39q31yk8lU0+>d@*nCfe}`_t&`QQ zH<6~3gSJ=&8#VKCa0j?N6kVy4n6U)BT6;*XdFHp+<8V^%s#hPLQUpy53h_GlQ2 z9(dG*91p|8E;N6yzg8z44B=z|3uHFsU~mNDU_)85L{dOmsuc1gHWW15mHoxw+|EE) z;2<5)t6BJocM!m==(KhUWy#x{;;}eHg8ypz6kvYs23B==_4<+Wero9^=HuqpmVV;f z864WBJ0xY*!6ZUuFlEN-({Xb%x_uh!`}Q_VF76+7?D=k(!A-Q0wtKjbt7F57>Zo4B zSPK&gMfu{qZaT(M{^~Sxf6SwG9al5NC|dsLMjXAfL<9v*`HIQy;VH0qiKgl}tI{}3 zihsc(u8%2fcgy23lFfsQl!hKQV!8LS&*b4A%4IP_J@*Q?Zvr|kHOx?NhwfIY{Ku=o z3XO??W*L0KlmAZ`xM=l zf(0b6ns#4(zWG?mR_j(%*)T$2TE*E+|D&t(RQC7by3gmWY{mKO$9_X>si&rvaNFI) zMdqg|*RXGJ$=Htf=TFVg=iA-UrFHX9drq0C!|DA)UEZ#om}-3{2Q|a}*AL_kJf5yD zEdqz^&~x1Kw)Ba`>g|0><7?gDIkJ}OMlTJWaMfMw+TTnC{WR*pAx@n-ZI&$gRaKFu z$(m%3>Aw#PbdVSg9af`zGJCqYrE;68R~8GBrU7!eg>n)R7Bsks1S6}pSFBpjtV9Qs zy%e(yBTt+&0aBK3k0)hJ>&*+jn9o!$NCUt$K<6EzbY#`&>UZvJwn3RKsxRE5qnGPm z2V_Uw!T9QSEiTb1Emjf4rAI4T(n4PjT2j752~5`ou6>F4eZ<9Dte(uCQnUYqX5{k{ z)UHCZwh!b_VnDn*8Duhf|2bWq)tG#bu$^$YKlmUbqDU>QAsggtfOq@Qokd@~miUgvV}@K9ZPj=ID)e3`|dbZCi_diM>AGeo;a zaSQk4$9=Jpvtq*BJYa;25MruIOj@FBI6{BFC|S2g$kV#kMNeI=n8!AI{I$=Ub5}zD{mo4h_2E!uvJ2>d&rdKA zIY}^ZG!RG-XrLpAKpGw8AN9U~fW!iKm1F(eHHNm<*2XqYj>2wE#x_RAMt}WNn7nC| zL5DVYNp_Bh{21;Zl$iSi-EyUD@f5$CS*9k!+9-o>!-r*CcL)QtB^>7Jf3NM#o=rU?`Z`O5rL7IOh7}v=G-sSbSsFeO zY*Y8_`hp+JJ$y4biUP0AC25|QlF^K3J+Un+=}DmjBX47eInJrGV%4H@L{!8Nnd*{A zR%>YR6{E!Q(hO(I6ua$(?D4{LaBQVdrI~=$cBMLHX-eW%zl5aSzLEAZimHVJTujM? zL_Ic3XJIZ&fzMTq<3p*EZxpwWlJI_!*Gq*Z%sJANoLO`W;mEZoLR0H-Nr9Rz67$AG zYCSz*2*d(y?l6Mv#Sd>=Y6&Ls+{egMs4TrK`dkQQMb37$F49|*T9g%)J<$}erPkv? zDETm*g$q1+iw|UzAht{$f8ka4Zf`$nuL!zYaARSxO4$dh3E9Y*aOyX-=;IU4*zy(DGs!ZJh~oxiTbPCPwRm zjL)-L-m)p8U5jL~hF6PMb_@kpZIH=Z8hTF5-?^s#B453x?0%X|($nx)q@mrSamIo0dE%rlnssMT8^3|ecIhWp*vwBbUpZ7T4 z7)~N=I-{G(M)ViH`>-!16^hW4X2qQS31&yfv;1AxT0Bk9{gd%-#jl3SEUpHzMY?$) z`d(C-TIV~dpP&mlVPk29A-9u#QH8lFSw_+95+Ly`h^MTsvlGe{qaSMEk$x#2#Hya4lhs>B)hmmFaUDqnu2HHbuya5r?R zRZb{v0gq4)nro1zrQ+Q&WCR6|SMu7>8!P-(HKm*hXLMs*>0^@x9qCCe^?2@QyK*GI@yFj|=pWHAA zLWvTOZAOp^L)I8#>xpe9P}-4x`N722@M2|)JC=h4bu{1PoySd>nfbDl0}QQH-?KdZ zT)yEgMEjWHxr58F7|CW_oGBf@JD|xvCJ60HZhIIy>q@!Nb4k2uLl#NlZhhChaag`d7w#?W!N5?;MFu5qL!+G8e!4wvanr#WPMAR9UeTBr1 z7);5tBSJ+@+5_fuX<4btL{;7dvA+uEnk-N=2uC^1+>EA@qcWZ;Y533!wnOmvvTG1K_^wTnWwGOOAx#t?PN%Lr8wGJ8t7*bQ%>x3#nr2%C01|6 zHPatz!p>Zyk_#YqO*zSUN-TM_bI>uy(Q%N!M&!B~_xiMJkA5UwOV#kJV5sBkdSmFg zVlNZS8b)6?#i5U;q^j`==gPAo;5r{022l|e$gHKaF8m~>%LAFdtg7xFExvkzKTExG zUJp&g#8)I&on?xoUL9-kf)GhssE~1lPSulf_&ShmryCN@-YI<&4xI^mp7cU&O@?E7 zOvM(TOTmkwalj)C}9JEnxD8c0Ah(txp)A!mWRU#h?nOFUwo|#f%oGCRLek65I>EZtId^Pi0iSo;9pkP`74R zrm{xPeE2dDwmnOy{De*fPH}~JeDIn~`OOv{^9`M%+bYW@XQOtNCG6OpD{hWr)O4?} z*UP(pZH}l@(y$yEi48}p^<%1+Xabw;_Jvv77jkUncFJ`OYm1bpQG_Z1Yas}l%pRU_Klq-o1=;w~so zY-V4KrdGY}t(?zJIf0#pLy(bSTn5U5`jU#Rz2%1$4qcm1cq^rCIqc)vP6svNy|af+ zN-8=x)K)1z&cEbp)N@*>J3xMI`vUoe8wQ=F&`VJofa}hCZN)Z=l_chAx{9C@C4cCJgkqw|J9-C0=;vlJp znRaFCZ|mR|(kVd~!7e$cTtYLg!T6GPv7+g>JTWbO5|H%ZS4*oQZ`5nbH0>8VE2lL; z7+b>Hkpw=wPj{CHC(}H`?YyOC1jtZrNo;hH9(uzQjTz?tU!dXEzK|_Jo?0l|H+6`w z2Z#2Lw(5C_>ZOCnPD|RPgvbLMQ)(!AU@!r|>}_^1})>L!tI#^M3;U zxiAy;-y;VE$Y0aW%APEL@7)(^)lOn88>M&`@-mw7RIE{-5@$3kT`#f&v#b$zx$JY6 z5M0E5-#xz~-wGA}lhgX*XPM3?=m=*M6Y z5p0|+!HeN&Pz0TafELTh;ClGt`JC&)L#doO{81~Oeo`O@F|$UciRH;@?$L{MZDGr$ zr$U!~%z!`KJhx3L9H2hqX~$&e@+?bUt=N3o%>j|ek?h<3)a7qc6hBl| zF~?@%Xxa1LwSYjNz;Bs>)nnoAhIV#BI60m(b+vMIJ7v z(9a?m-3nMHpGN9S-{IG;>ej2=35bp0FJ)88}yxf^b~?tVIDHyl>VEdy_!fYM^n4I~hUE`O07YjE}mJzFRLgx9JgHcP`*DcU*Bp2r-EG-P7|zqzS?x=; z3S*4Z7U`Xtl8;oW@U96+87^eUr3|Q4ZTHFmBl)KaZm!`zt^1c)ApD;S{zV1UG*=S5 zui!hgo2<}x+A$Qr3u=M>*a(r%9j%#YS;5G5Eh5bf3|kg?=nu$r=l9Ev!xt4`tf#Z91b{y}kU*MWc%bk4f5J_hN|$5-SIv@Q!>tjr1T8^&iuLpIQO89Kg}GR!&yu4qgxTckBTe2ZaWTp8$c_0mmI3 zz!{ET6mExl``GXn>07vPU?xU^@1rvj2K@#9ITMFTj-83d@6DhRz;ZT#>8Kc~ctM~) z`O4p!3brYn5{dHT0KsK|0CL0`XZWxQ4r<$D{c*E4&mV>Pl+YGY= zhN}sH335Q4a?}iJHEeX??8kkvCE^ITv8rUocu^QWq&})oMX8<#%|KSaG zU2K8_Gmd4canDb41sb<`vDnKaP4CzABHf+Ltz3b#pW_5780V59g94hrp~N7vBXgo+ z@>^Z#8;-}XE$p+VKluC)zra{0PuNbtJ`(xEpw;$^;rH@7d_aOtgWbaZLo?BVMf0nn zjm?1FTKvNh(fy0zNTU%Of3!XG2Mq#+_JTnFXx3oEkJiqI;e7pv;J+$qZ1mCE;V{~6 z=&+QIt0b_wj%J{TxtvFi=lYY6VsjnMV-9n Date: Sun, 28 Apr 2024 08:32:31 +0800 Subject: [PATCH 064/134] Add extensive module information to module-info.java A comprehensive documentation has been added to the module-info.java file for the 'pro.verron.officestamper' application. This includes declaring its dependencies, packages needed for runtime access and packages that need to be exported. A warning has also been added about certain packages opening and exporting that should be inaccessible in future versions. --- src/main/java/module-info.java | 41 +++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 8a2cad50..c03ecf3c 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,3 +1,39 @@ +/** + * This module serves as the main module for the "pro.verron.officestamper" application. + * It declares the module dependencies and exports the necessary packages. + *

+ * The module requires the following modules: + * - spring.core + * - spring.expression + * - org.docx4j.core + *

+ * It also requires the following modules statically: + * - org.apache.commons.io + * - org.slf4j + * - jakarta.xml.bind + *

+ * The module opens the following packages for reflection and runtime access: + * - pro.verron.docxstamper.api + * - pro.verron.docxstamper.preset + *

+ * The module exports the following packages for use by other modules: + * - pro.verron.docxstamper.api + * - pro.verron.docxstamper.preset + *

+ * Additionally, it opens the "pro.verron.docxstamper.core" package to the "pro.verron.officestamper.test" module, + * and exports it for use by the same module. + *

+ * WARNING: The module also opens and exports packages that should be innacessible in the next version. + * These packages are: + * - pro.verron.docxstamper + * - org.wickedsource.docxstamper + * - org.wickedsource.docxstamper.api + * - org.wickedsource.docxstamper.api.commentprocessor + * - org.wickedsource.docxstamper.el + * - org.wickedsource.docxstamper.util + * - org.wickedsource.docxstamper.processor + * - org.wickedsource.docxstamper.processor.table + */ module pro.verron.officestamper { requires spring.core; requires spring.expression; @@ -16,9 +52,8 @@ opens pro.verron.docxstamper.core to pro.verron.officestamper.test; exports pro.verron.docxstamper.core to pro.verron.officestamper.test; - /** - * TODO: remove all the following exports in next version - */ + + // TODO_LATER: remove all the following exports in next version opens pro.verron.docxstamper; exports pro.verron.docxstamper; exports org.wickedsource.docxstamper; From 46883d71c5e46205304589bb54cc93ba970ecba2 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 28 Apr 2024 08:33:03 +0800 Subject: [PATCH 065/134] Refactor code for better readability in Examples.java Reorganized code in the Examples.java file to improve clarity. Changed multiline strings into text blocks for easier readability. Also restructured arguments inside the 'stamper.stamp' method for a cleaner look. --- .../java/pro/verron/officestamper/Examples.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/officestamper-examples/src/main/java/pro/verron/officestamper/Examples.java b/officestamper-examples/src/main/java/pro/verron/officestamper/Examples.java index 3c87c282..a6bf1078 100644 --- a/officestamper-examples/src/main/java/pro/verron/officestamper/Examples.java +++ b/officestamper-examples/src/main/java/pro/verron/officestamper/Examples.java @@ -15,6 +15,7 @@ public class Examples { public static void stampDiagnostic(OutputStream outputStream) { logger.info("Start of the diagnostic stamping procedure"); + logger.info("Setup a map-reading able docx-stamper instance"); var configuration = new DocxStamperConfiguration() .setEvaluationContextConfigurer(enableMapAccess()); @@ -23,10 +24,11 @@ public static void stampDiagnostic(OutputStream outputStream) { logger.info("Load the internally packaged 'Diagnostic.docx' template resource"); var template = Utils.streamResource("Diagnostic.docx"); - logger.info("Create a context with: " - + "system environment variables, " - + "jvm properties, " - + "and user preferences"); + logger.info(""" + Create a context with: \ + system environment variables, \ + jvm properties, \ + and user preferences"""); var diagnosticMaker = new Diagnostic(); var context = Map.of( @@ -38,11 +40,7 @@ public static void stampDiagnostic(OutputStream outputStream) { ); logger.info("Start stamping process"); - stamper.stamp( - template, - context, - outputStream - ); + stamper.stamp(template, context, outputStream); logger.info("End of the diagnostic stamping procedure"); } From 0d33705b41433ee660ae39af5dc35a06e8161be0 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 28 Apr 2024 08:44:08 +0800 Subject: [PATCH 066/134] Upgrade docx-stamper version to 1.6.8 The version of the dependency docx-stamper used in the officestamper-examples project has been updated. This aims to take advantage of any bug fixes, improvements, or updates available in the newer version. --- officestamper-examples/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/officestamper-examples/pom.xml b/officestamper-examples/pom.xml index 2963ef5a..563f188e 100644 --- a/officestamper-examples/pom.xml +++ b/officestamper-examples/pom.xml @@ -18,7 +18,7 @@ pro.verron docx-stamper - 1.6.7 + 1.6.8 From cc8736fc03f61e7af64cbba25c90360ff1a4ad58 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Tue, 30 Apr 2024 09:02:30 +0800 Subject: [PATCH 067/134] Update deprecated method advice in DocxStamper This commit updates the deprecated method advice in the DocxStamper class, recommending the use of only OfficeStampers#docxStamper() method. Additionally, it formats the commentProcessors declaration in the DocxStamperConfiguration class for better readability. --- src/main/java/org/wickedsource/docxstamper/DocxStamper.java | 2 +- .../org/wickedsource/docxstamper/DocxStamperConfiguration.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java index e2aa1d14..6e6f499a 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java @@ -51,7 +51,7 @@ public class DocxStamper /** * Creates a new DocxStamper with the default configuration. * - * @deprecated since 1.6.4, use {@link OfficeStampers#docxStamper()} or {@link OfficeStampers#nopreprocessingDocxStamper()} instead. + * @deprecated since 1.6.4, use {@link OfficeStampers#docxStamper()} instead. */ @Deprecated(since = "1.6.4", forRemoval = true) public DocxStamper() { diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java index b3b1c788..45366b85 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java @@ -45,7 +45,8 @@ public class DocxStamperConfiguration implements OfficeStamperConfiguration { - private final Map, Function> commentProcessors = new HashMap<>(); + private final Map, Function> commentProcessors = + new HashMap<>(); private final List resolvers = new ArrayList<>(); private final Map, Object> expressionFunctions = new HashMap<>(); private final List preprocessors = new ArrayList<>(); From c240f1740172ed919059477dfb5593a50e352432 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Tue, 30 Apr 2024 09:03:41 +0800 Subject: [PATCH 068/134] Update exception handling and import changes The exception handling class `CommentProcessingException` has been updated to extend `OfficeStamperException` instead of `DocxStamperException`. Meanwhile, the `StampTable` class has new import statements and added documentation. --- .../docxstamper/processor/CommentProcessingException.java | 6 +++--- .../docxstamper/processor/table/StampTable.java | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessingException.java b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessingException.java index aa043195..1560c8f6 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessingException.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessingException.java @@ -1,7 +1,7 @@ package org.wickedsource.docxstamper.processor; import org.docx4j.wml.P; -import org.wickedsource.docxstamper.api.DocxStamperException; +import pro.verron.docxstamper.api.OfficeStamperException; import static java.lang.String.format; import static org.docx4j.TextUtils.getText; @@ -14,7 +14,8 @@ * @version ${version} * @since 1.0.0 */ -public class CommentProcessingException extends DocxStamperException { +public class CommentProcessingException + extends OfficeStamperException { /** *

Constructor for CommentProcessingException.

@@ -25,5 +26,4 @@ public class CommentProcessingException extends DocxStamperException { public CommentProcessingException(String message, P paragraph) { super(format("%s : %s", message, getText(paragraph))); } - } diff --git a/src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java b/src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java index 6b442464..39c1d0c0 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java @@ -14,10 +14,16 @@ @Deprecated(since = "1.6.8", forRemoval = true) public class StampTable extends pro.verron.docxstamper.api.StampTable { + /** + * {@inheritDoc} + */ public StampTable() { super(); } + /** + * {@inheritDoc} + */ public StampTable( @NonNull List headers, @NonNull List> records From f90fd1bcbf9a4a083f2445c1daf265996a2d3471 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Tue, 30 Apr 2024 09:04:05 +0800 Subject: [PATCH 069/134] Change superclass of UnresolvedExpressionException Updated the superclass of UnresolvedExpressionException from DocxStamperException to OfficeStamperException. This update was necessary to align with the new exception handling strategy. --- .../docxstamper/api/UnresolvedExpressionException.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/api/UnresolvedExpressionException.java b/src/main/java/org/wickedsource/docxstamper/api/UnresolvedExpressionException.java index d30858b4..314ff72f 100644 --- a/src/main/java/org/wickedsource/docxstamper/api/UnresolvedExpressionException.java +++ b/src/main/java/org/wickedsource/docxstamper/api/UnresolvedExpressionException.java @@ -1,5 +1,7 @@ package org.wickedsource.docxstamper.api; +import pro.verron.docxstamper.api.OfficeStamperException; + /** * This exception is thrown if an expression could not be processed by any comment processor. * @@ -8,7 +10,8 @@ * @version ${version} * @since 1.0.3 */ -public class UnresolvedExpressionException extends DocxStamperException { +public class UnresolvedExpressionException + extends OfficeStamperException { /** *

Constructor for UnresolvedExpressionException.

* @@ -16,6 +19,7 @@ public class UnresolvedExpressionException extends DocxStamperException { * @param cause the root cause for this exception */ public UnresolvedExpressionException(String expression, Throwable cause) { - super(String.format("The following expression could not be processed by any comment processor: %s", expression), cause); + super(String.format("The following expression could not be processed by any comment processor: %s", expression), + cause); } } From 4eb245e74c647d29b563d016cb59218d923d1efe Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Tue, 30 Apr 2024 09:08:26 +0800 Subject: [PATCH 070/134] Add Javadoc documentation to multiple classes Javadoc documentation has been added for better clarity and understanding of the codebase. Most classes and interfaces have been documented including CommentProcessor, Paragraph, ObjectResolver, and OfficeStamperConfiguration among others. The objective has been to enhance readability and code maintainability by providing useful information about the role and behavior of each class or interface. --- .../api/AbstractCommentProcessor.java | 14 +- .../pro/verron/docxstamper/api/Comment.java | 92 +++++++++++- .../docxstamper/api/CommentProcessor.java | 3 + .../pro/verron/docxstamper/api/Image.java | 15 ++ .../docxstamper/api/ObjectResolver.java | 6 +- .../api/OfficeStamperConfiguration.java | 134 +++++++++++++++++- .../api/OfficeStamperException.java | 28 ++++ .../pro/verron/docxstamper/api/Paragraph.java | 16 +++ .../api/ParagraphPlaceholderReplacer.java | 12 ++ .../verron/docxstamper/api/Placeholder.java | 14 ++ .../verron/docxstamper/api/PreProcessor.java | 4 + 11 files changed, 329 insertions(+), 9 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/api/AbstractCommentProcessor.java b/src/main/java/pro/verron/docxstamper/api/AbstractCommentProcessor.java index db2f3a20..96aa5453 100644 --- a/src/main/java/pro/verron/docxstamper/api/AbstractCommentProcessor.java +++ b/src/main/java/pro/verron/docxstamper/api/AbstractCommentProcessor.java @@ -6,6 +6,11 @@ import java.util.Objects; +/** + * AbstractCommentProcessor is an abstract base class for comment processors. + * It implements the CommentProcessor interface. + * It provides common functionality and fields that subclasses can use. + */ public abstract class AbstractCommentProcessor implements CommentProcessor { /** @@ -17,7 +22,14 @@ public abstract class AbstractCommentProcessor private Comment currentComment; private WordprocessingMLPackage document; - public AbstractCommentProcessor(ParagraphPlaceholderReplacer placeholderReplacer) {this.placeholderReplacer = placeholderReplacer;} + /** + * Creates an instance of AbstractCommentProcessor with the given ParagraphPlaceholderReplacer. + * + * @param placeholderReplacer the ParagraphPlaceholderReplacer used to replace expressions in the comment text + */ + public AbstractCommentProcessor(ParagraphPlaceholderReplacer placeholderReplacer) { + this.placeholderReplacer = placeholderReplacer; + } /** *

Getter for the field currentCommentWrapper.

diff --git a/src/main/java/pro/verron/docxstamper/api/Comment.java b/src/main/java/pro/verron/docxstamper/api/Comment.java index ce2d825c..75754d47 100644 --- a/src/main/java/pro/verron/docxstamper/api/Comment.java +++ b/src/main/java/pro/verron/docxstamper/api/Comment.java @@ -6,32 +6,120 @@ import java.util.List; import java.util.Set; +/** + * The Comment interface provides methods for managing comments in a document. + */ public interface Comment { + + /** + * Retrieves the parent of the comment. + * + * @return the parent of the comment + */ ContentAccessor getParent(); + /** + * Retrieves the elements in the document that are between the comment range anchors. + * + * @return a list of objects representing the elements between the comment range anchors. + * TODO: Rename to be a more generic + */ List getRepeatElements(); - WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) throws Exception; - + /** + * Creates a new document containing only the elements between the comment range anchors. + * + * @param document the document from which to copy the elements. + * + * @return a new document containing only the elements between the comment range anchors. + * + * @throws Exception if the sub template could not be created. + * TODO: Remove from this interface and move to an utility class + */ + WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) + throws Exception; + + /** + * Tries to build a subtemplate from the given WordprocessingMLPackage document. + * + * @param document the source document from which to build the subtemplate + * @return the built subtemplate as a WordprocessingMLPackage + * TODO: Remove from this interface and move to an utility class + */ WordprocessingMLPackage tryBuildingSubtemplate(WordprocessingMLPackage document); + /** + * Retrieves the {@link CommentRangeEnd} object associated with this comment. + * + * @return the {@link CommentRangeEnd} object associated with this comment + */ CommentRangeEnd getCommentRangeEnd(); + /** + * Sets the {@link CommentRangeEnd} object associated with this comment. + * + * @param commentRangeEnd the {@link CommentRangeEnd} object to set + * TODO: Remove the setting method from interface to increase immutability + */ void setCommentRangeEnd(CommentRangeEnd commentRangeEnd); + /** + * Retrieves the CommentRangeStart object associated with this comment. + * + * @return the CommentRangeStart object associated with this comment + */ CommentRangeStart getCommentRangeStart(); + /** + * Sets the CommentRangeStart object associated with this comment. + * + * @param commentRangeStart the CommentRangeStart object to set + * TODO: Remove the setting method from interface to increase immutability + */ void setCommentRangeStart(CommentRangeStart commentRangeStart); + /** + * Retrieves the {@link R.CommentReference} object associated with this comment. + * + * @return the {@link R.CommentReference} object associated with this comment + */ R.CommentReference getCommentReference(); + /** + * Sets the comment reference for this comment. + * + * @param commentReference the comment reference to set + * TODO: Remove the setting method from interface to increase immutability + */ void setCommentReference(R.CommentReference commentReference); + /** + * Retrieves the children of the comment. + * + * @return a set of Comment objects representing the children of the comment + */ Set getChildren(); + /** + * Sets the children of the comment. + * + * @param comments the set of Comment objects representing the children of the comment + * TODO: Remove the setting method from interface to increase immutability + */ void setChildren(Set comments); + /** + * Retrieves the comment associated with this object. + * + * @return the comment associated with this object + */ Comments.Comment getComment(); + /** + * Sets the comment for this object. + * + * @param comment the comment to set + * TODO: Remove the setting method from interface to increase immutability + */ void setComment(Comments.Comment comment); } diff --git a/src/main/java/pro/verron/docxstamper/api/CommentProcessor.java b/src/main/java/pro/verron/docxstamper/api/CommentProcessor.java index 0b44dc39..9e37803d 100644 --- a/src/main/java/pro/verron/docxstamper/api/CommentProcessor.java +++ b/src/main/java/pro/verron/docxstamper/api/CommentProcessor.java @@ -4,6 +4,9 @@ import org.docx4j.wml.P; import org.docx4j.wml.R; +/** + * CommentProcessor is an interface that defines the methods for processing comments in a .docx template. + */ public interface CommentProcessor { /** * This method is called after all comments in the .docx template have been passed to the comment processor. diff --git a/src/main/java/pro/verron/docxstamper/api/Image.java b/src/main/java/pro/verron/docxstamper/api/Image.java index 494984c8..838af604 100644 --- a/src/main/java/pro/verron/docxstamper/api/Image.java +++ b/src/main/java/pro/verron/docxstamper/api/Image.java @@ -6,10 +6,25 @@ import java.io.IOException; import java.io.InputStream; +/** + * This class describes an image which will be inserted into a document. + * + * @author Joseph Verron + * @author Romster + * @version ${version} + * @since 1.0.0 + */ public sealed class Image permits org.wickedsource.docxstamper.replace.typeresolver.image.Image { + /** + * todo: make private asap + */ protected final byte[] imageBytes; + + /** + * todo: make private asap + */ protected Integer maxWidth; /** diff --git a/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java b/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java index c9130f9a..82a33128 100644 --- a/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java +++ b/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java @@ -23,8 +23,10 @@ public interface ObjectResolver { * @param document the {@link WordprocessingMLPackage} document in * which to resolve the expression * @param placeholder the expression value to be replaced - * @param object the object to be used for resolving the expression + * @param object the object to be used for resolving the expression + * * @return the resolved value for the expression + * * @throws DocxStamperException if no resolver is found for the object */ default R resolve( @@ -57,7 +59,9 @@ class Log { * which to resolve the expression * @param expression the expression value to be replaced * @param object the object to be used for resolving the expression + * * @return the resolved value for the expression + * * @throws DocxStamperException if no resolver is found for the object */ R resolve( diff --git a/src/main/java/pro/verron/docxstamper/api/OfficeStamperConfiguration.java b/src/main/java/pro/verron/docxstamper/api/OfficeStamperConfiguration.java index 582bd165..5f0ca4fe 100644 --- a/src/main/java/pro/verron/docxstamper/api/OfficeStamperConfiguration.java +++ b/src/main/java/pro/verron/docxstamper/api/OfficeStamperConfiguration.java @@ -2,66 +2,190 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.springframework.expression.spel.SpelParserConfiguration; -import org.springframework.lang.NonNull; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.Function; +/** + * Represents the configuration for the OfficeStamper class. + */ public interface OfficeStamperConfiguration { - @Deprecated(since = "1.6.7") + /** + * Retrieves the null replacement value. + * + * @return an Optional containing the null replacement value, if it exists; otherwise, an empty Optional. + * + * @deprecated since version 1.6.7 + */ + @Deprecated(since = "1.6.7", forRemoval = true) Optional nullReplacementValue(); + /** + * Checks if the failOnUnresolvedExpression flag is set to true or false. + * + * @return true if failOnUnresolvedExpression is set to true, false otherwise. + */ boolean isFailOnUnresolvedExpression(); + /** + * Sets the failOnUnresolvedExpression flag to determine whether unresolved expressions should + * cause an exception to be thrown. + * + * @param failOnUnresolvedExpression flag indicating whether to fail on unresolved expressions + * + * @return the updated OfficeStamperConfiguration object + */ OfficeStamperConfiguration setFailOnUnresolvedExpression(boolean failOnUnresolvedExpression); + /** + * Retrieves a new OfficeStamperConfiguration object with the provided null replacement value. + * + * @param nullValuesDefault the null replacement value. + * + * @return OfficeStamperConfiguration object with the provided null replacement value. + * + * @deprecated since version 1.6.7 + */ @Deprecated(since = "1.6.7", forRemoval = true) OfficeStamperConfiguration nullValuesDefault(String nullValuesDefault); + /** + * Replaces null values in the OfficeStamperConfiguration object. + * + * @param replaceNullValues flag indicating whether to replace null values + * + * @return the updated OfficeStamperConfiguration object + * + * @deprecated since version 1.6.7 + */ @Deprecated(since = "1.6.7", forRemoval = true) OfficeStamperConfiguration replaceNullValues(boolean replaceNullValues); + /** + * Sets the default value for unresolved expressions in the OfficeStamperConfiguration object. + * + * @param unresolvedExpressionsDefaultValue the default value for unresolved expressions + * + * @return the updated OfficeStamperConfiguration object + */ OfficeStamperConfiguration unresolvedExpressionsDefaultValue(String unresolvedExpressionsDefaultValue); + /** + * Replaces unresolved expressions in the OfficeStamperConfiguration object. + * + * @param replaceUnresolvedExpressions flag indicating whether to replace unresolved expressions + * + * @return the updated OfficeStamperConfiguration object + */ OfficeStamperConfiguration replaceUnresolvedExpressions( boolean replaceUnresolvedExpressions ); + /** + * Configures whether to leave empty on expression error. + * + * @param leaveEmpty boolean value indicating whether to leave empty on expression error + * + * @return the updated OfficeStamperConfiguration object + */ OfficeStamperConfiguration leaveEmptyOnExpressionError( boolean leaveEmpty ); - @Deprecated(since = "1.6.7", forRemoval = true) - OfficeStamperConfiguration addTypeResolver( + /** + * Adds a type resolver to the OfficeStamperConfiguration. + * A type resolver is responsible for mapping an object of a certain Java class to an object of the DOCX4J api that + * can be put into the .docx document. Type resolvers are used to replace expressions within the .docx template. + * + * @param resolvedType the Java class that the type resolver is responsible for. + * @param resolver the implementation of {@link ITypeResolver} that resolves objects of the given type. + * + * @return the updated OfficeStamperConfiguration object. + * + * @deprecated as of version 1.6.7, replaced by {@link ObjectResolver}. + * The new resolver is more versatile, requires less reflection mechanism, + * and simplifies the internal workings of the docx-stamper project. + */ + @Deprecated(since = "1.6.7", forRemoval = true) OfficeStamperConfiguration addTypeResolver( Class resolvedType, ITypeResolver resolver ); + /** + * Exposes an interface to the expression language. + * + * @param interfaceClass the interface class to be exposed + * @param implementation the implementation object of the interface + * + * @return the updated OfficeStamperConfiguration object + */ OfficeStamperConfiguration exposeInterfaceToExpressionLanguage( Class interfaceClass, Object implementation ); + /** + * Adds a comment processor to the OfficeStamperConfiguration. A comment processor is responsible for + * processing comments in the document and performing specific operations based on the comment content. + * + * @param interfaceClass the interface class associated with the comment processor + * @param commentProcessorFactory a function that creates a CommentProcessor object based on the + * ParagraphPlaceholderReplacer implementation + * + * @return the updated OfficeStamperConfiguration object + */ OfficeStamperConfiguration addCommentProcessor( Class interfaceClass, Function commentProcessorFactory ); + /** + * Retrieves the OfficeStamper configured with the current settings. + * + * @return the OfficeStamper configured with the current settings. + * + * @since 1.6.4 + * @deprecated This method is marked for removal and should not be used. Consider using a different method instead. + */ @Deprecated(forRemoval = true, since = "1.6.4") OfficeStamper build(); + /** + * Adds a pre-processor to the OfficeStamperConfiguration. A pre-processor is responsible for + * processing the document before the actual processing takes place. + * + * @param preprocessor the pre-processor to add + */ void addPreprocessor(PreProcessor preprocessor); + /** + * Determines whether unresolved expressions in the OfficeStamper configuration should be replaced. + * + * @return true if unresolved expressions should be replaced, false otherwise. + */ boolean isReplaceUnresolvedExpressions(); + /** + * Determines whether to leave empty on expression error. + * + * @return true if expression errors are left empty, false otherwise + */ boolean isLeaveEmptyOnExpressionError(); + /** + * Retrieves the default value for unresolved expressions. + * + * @return the default value for unresolved expressions + */ String getUnresolvedExpressionsDefaultValue(); + /** + * TODO:javadoc + */ String getLineBreakPlaceholder(); OfficeStamperConfiguration setLineBreakPlaceholder( - @NonNull String lineBreakPlaceholder + String lineBreakPlaceholder ); EvaluationContextConfigurer getEvaluationContextConfigurer(); diff --git a/src/main/java/pro/verron/docxstamper/api/OfficeStamperException.java b/src/main/java/pro/verron/docxstamper/api/OfficeStamperException.java index d075855c..b18ba383 100644 --- a/src/main/java/pro/verron/docxstamper/api/OfficeStamperException.java +++ b/src/main/java/pro/verron/docxstamper/api/OfficeStamperException.java @@ -1,11 +1,39 @@ package pro.verron.docxstamper.api; +/** + * OfficeStamperException is a subclass of RuntimeException that represents an exception that can be thrown during the + * processing of an Office document using the OfficeStamper library + * . + * It provides additional constructors to handle different scenarios. + */ public class OfficeStamperException extends RuntimeException { + /** + * OfficeStamperException is a subclass of RuntimeException that represents an exception that can be thrown + * during the processing of an Office document using the OfficeStamper + * library. + * + * @param message a message describing the error + */ public OfficeStamperException(String message) {super(message);} + /** + * OfficeStamperException is a subclass of RuntimeException that represents an exception that can be thrown + * during the processing of an Office document using the OfficeStamper + * library. + * + * @param cause the cause of the exception + */ public OfficeStamperException(Throwable cause) {super(cause);} + /** + * OfficeStamperException is a subclass of RuntimeException that represents an exception that can be thrown + * during the processing of an Office document using the OfficeStamper + * library. + * + * @param message a message describing the error + * @param cause the cause of the exception + */ public OfficeStamperException(String message, Throwable cause) { super(message, cause); diff --git a/src/main/java/pro/verron/docxstamper/api/Paragraph.java b/src/main/java/pro/verron/docxstamper/api/Paragraph.java index f3d8e858..d29292ff 100644 --- a/src/main/java/pro/verron/docxstamper/api/Paragraph.java +++ b/src/main/java/pro/verron/docxstamper/api/Paragraph.java @@ -1,7 +1,23 @@ package pro.verron.docxstamper.api; +/** + * The Paragraph interface represents a paragraph in a text document. + * It provides methods for replacing a placeholder within the paragraph and retrieving the paragraph as a string. + */ public interface Paragraph { + + /** + * Replaces a placeholder in the given paragraph with the specified replacement. + * + * @param placeholder The placeholder to be replaced. + * @param replacement The replacement for the placeholder. + */ void replace(Placeholder placeholder, T replacement); + /** + * Returns the paragraph as a string. + * + * @return the paragraph as a string + */ String asString(); } diff --git a/src/main/java/pro/verron/docxstamper/api/ParagraphPlaceholderReplacer.java b/src/main/java/pro/verron/docxstamper/api/ParagraphPlaceholderReplacer.java index b69b3a31..79f09819 100644 --- a/src/main/java/pro/verron/docxstamper/api/ParagraphPlaceholderReplacer.java +++ b/src/main/java/pro/verron/docxstamper/api/ParagraphPlaceholderReplacer.java @@ -2,7 +2,19 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; +/** + * The ParagraphPlaceholderReplacer interface represents an object that can resolve expressions in a paragraph + * and replace them with values provided by an expression resolver. + */ public interface ParagraphPlaceholderReplacer { + + /** + * Finds expressions in the given paragraph and replaces them with the values provided by the expression resolver. + * + * @param paragraph the paragraph in which to replace expressions + * @param context the context root + * @param document the document in which to replace all expressions + */ void resolveExpressionsForParagraph( Paragraph paragraph, Object context, diff --git a/src/main/java/pro/verron/docxstamper/api/Placeholder.java b/src/main/java/pro/verron/docxstamper/api/Placeholder.java index e376710b..ef7cfd1c 100644 --- a/src/main/java/pro/verron/docxstamper/api/Placeholder.java +++ b/src/main/java/pro/verron/docxstamper/api/Placeholder.java @@ -1,7 +1,21 @@ package pro.verron.docxstamper.api; +/** + * The Placeholder interface represents a placeholder in a text document. + * It provides methods to retrieve the content of the placeholder and the full expression. + */ public interface Placeholder { + /** + * Returns the content of the placeholder. + * + * @return the content of the placeholder + */ String content(); + /** + * Retrieves the expression of the placeholder. + * + * @return the expression of the placeholder. + */ String expression(); } diff --git a/src/main/java/pro/verron/docxstamper/api/PreProcessor.java b/src/main/java/pro/verron/docxstamper/api/PreProcessor.java index a78b9d85..4347f62a 100644 --- a/src/main/java/pro/verron/docxstamper/api/PreProcessor.java +++ b/src/main/java/pro/verron/docxstamper/api/PreProcessor.java @@ -2,6 +2,10 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; +/** + * An interface for pre-processors that are called before the actual processing + * of a document takes place. + */ public interface PreProcessor { /** * Processes the given document before the actual processing takes place. From c927807231e822781f9171b698532e01f6ca4043 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Tue, 30 Apr 2024 09:09:44 +0800 Subject: [PATCH 071/134] Remove generic type from Paragraph interface The generic type was removed from the Paragraph interface to make it more flexible. Consequently, the replace method now accepts an Object as the replacement parameter, instead of a specific type T. --- src/main/java/pro/verron/docxstamper/api/Paragraph.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/api/Paragraph.java b/src/main/java/pro/verron/docxstamper/api/Paragraph.java index d29292ff..dee1f4df 100644 --- a/src/main/java/pro/verron/docxstamper/api/Paragraph.java +++ b/src/main/java/pro/verron/docxstamper/api/Paragraph.java @@ -4,7 +4,7 @@ * The Paragraph interface represents a paragraph in a text document. * It provides methods for replacing a placeholder within the paragraph and retrieving the paragraph as a string. */ -public interface Paragraph { +public interface Paragraph { /** * Replaces a placeholder in the given paragraph with the specified replacement. @@ -12,7 +12,7 @@ public interface Paragraph { * @param placeholder The placeholder to be replaced. * @param replacement The replacement for the placeholder. */ - void replace(Placeholder placeholder, T replacement); + void replace(Placeholder placeholder, Object replacement); /** * Returns the paragraph as a string. From 3f79b695a4dc48a9e3c41dc635edb15bd14dc876 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Tue, 30 Apr 2024 09:11:55 +0800 Subject: [PATCH 072/134] Remove Logger from Log class in ObjectResolver The Logger from the Log class in the ObjectResolver file has been removed. Now the LoggerFactory class directly creates the Logger instance when needed. This eliminates an unnecessary declaration and makes the code cleaner and lighter. --- .../pro/verron/docxstamper/api/ObjectResolver.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java b/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java index 82a33128..b4353b12 100644 --- a/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java +++ b/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java @@ -2,7 +2,6 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; -import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wickedsource.docxstamper.api.DocxStamperException; @@ -20,8 +19,8 @@ public interface ObjectResolver { /** * Resolves the expression in the given document with the provided object. * - * @param document the {@link WordprocessingMLPackage} document in - * which to resolve the expression + * @param document the {@link WordprocessingMLPackage} document in + * which to resolve the expression * @param placeholder the expression value to be replaced * @param object the object to be used for resolving the expression * @@ -36,7 +35,8 @@ default R resolve( ) { R resolution = resolve(document, placeholder.content(), object); var msg = "Expression '{}' replaced by '{}' with resolver {}"; - Log.log.debug(msg, placeholder, resolution, this); + LoggerFactory.getLogger(ObjectResolver.class) + .debug(msg, placeholder, resolution, this); return resolution; } @@ -44,14 +44,11 @@ default R resolve( * Checks if the given object can be resolved. * * @param object the object to be resolved + * * @return true if the object can be resolved, false otherwise */ boolean canResolve(Object object); - class Log { - static final Logger log = LoggerFactory.getLogger(ObjectResolver.class); - } - /** * Resolves the expression in the given document with the provided object. * @@ -69,4 +66,5 @@ R resolve( String expression, Object object ); + } From c0f2cc998dfaddf3682d276e5264ef6e2b23f39a Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Tue, 30 Apr 2024 09:14:15 +0800 Subject: [PATCH 073/134] Update OfficeStamperConfigurations with new presets The OfficeStamperConfigurations class was updated with the creation of static methods that provide different configurations for OfficeStamper. This includes a standard configuration, one with preprocessing, and a specific configuration for PowerPoint. A separate PowerpointStamperConfiguration class was also created. Furthermore, detailed comments were added for better understanding of the methods. --- .../preset/OfficeStamperConfigurations.java | 229 +++--------------- .../docxstamper/preset/OfficeStampers.java | 22 ++ .../PowerpointStamperConfiguration.java | 201 +++++++++++++++ 3 files changed, 252 insertions(+), 200 deletions(-) create mode 100644 src/main/java/pro/verron/docxstamper/preset/PowerpointStamperConfiguration.java diff --git a/src/main/java/pro/verron/docxstamper/preset/OfficeStamperConfigurations.java b/src/main/java/pro/verron/docxstamper/preset/OfficeStamperConfigurations.java index 652fe9c9..f3033429 100644 --- a/src/main/java/pro/verron/docxstamper/preset/OfficeStamperConfigurations.java +++ b/src/main/java/pro/verron/docxstamper/preset/OfficeStamperConfigurations.java @@ -1,21 +1,23 @@ package pro.verron.docxstamper.preset; -import org.docx4j.openpackaging.packages.WordprocessingMLPackage; -import org.springframework.expression.spel.SpelParserConfiguration; +import org.wickedsource.docxstamper.DocxStamperConfiguration; import org.wickedsource.docxstamper.preprocessor.MergeSameStyleRuns; import org.wickedsource.docxstamper.preprocessor.RemoveProofErrors; -import pro.verron.docxstamper.api.*; - -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; +import pro.verron.docxstamper.api.OfficeStamperConfiguration; +/** + * The OfficeStamperConfigurations class provides static methods + * to create different configurations for the OfficeStamper. + */ public class OfficeStamperConfigurations { - public static OfficeStamperConfiguration standard() { - return new org.wickedsource.docxstamper.DocxStamperConfiguration(); - } + /** + * Creates a new OfficeStamperConfiguration with the standard configuration and additional preprocessors. + * + * @return the OfficeStamperConfiguration + * + * @see OfficeStamperConfiguration + */ public static OfficeStamperConfiguration standardWithPreprocessing() { var configuration = standard(); configuration.addPreprocessor(new RemoveProofErrors()); @@ -23,195 +25,22 @@ public static OfficeStamperConfiguration standardWithPreprocessing() { return configuration; } - public static OfficeStamperConfiguration powerpoint() { - return new OfficeStamperConfiguration() { - @Override - public Optional nullReplacementValue() { - return Optional.empty(); - } - - @Override - public boolean isFailOnUnresolvedExpression() { - return false; - } - - @Override - public OfficeStamperConfiguration setFailOnUnresolvedExpression( - boolean failOnUnresolvedExpression - ) { - return null; - } - - @Override - public OfficeStamperConfiguration nullValuesDefault(String nullValuesDefault) { - return null; - } - - @Override - public OfficeStamperConfiguration replaceNullValues(boolean replaceNullValues) { - return null; - } - - @Override - public OfficeStamperConfiguration unresolvedExpressionsDefaultValue( - String unresolvedExpressionsDefaultValue - ) { - return null; - } - - @Override - public OfficeStamperConfiguration replaceUnresolvedExpressions( - boolean replaceUnresolvedExpressions - ) { - return null; - } - - @Override - public OfficeStamperConfiguration leaveEmptyOnExpressionError( - boolean leaveEmpty - ) { - return null; - } - - @Override - public OfficeStamperConfiguration addTypeResolver( - Class resolvedType, - ITypeResolver resolver - ) { - return null; - } - - @Override - public OfficeStamperConfiguration exposeInterfaceToExpressionLanguage( - Class interfaceClass, - Object implementation - ) { - return null; - } - - @Override - public OfficeStamperConfiguration addCommentProcessor( - Class interfaceClass, - Function commentProcessorFactory - ) { - return null; - } - - @Override - public OfficeStamper build() { - return null; - } - - @Override - public void addPreprocessor(PreProcessor preprocessor) { - - } - - @Override - public boolean isReplaceUnresolvedExpressions() { - return false; - } - - @Override - public boolean isLeaveEmptyOnExpressionError() { - return false; - } - - @Override - public String getUnresolvedExpressionsDefaultValue() { - return null; - } - - @Override - public String getLineBreakPlaceholder() { - return null; - } - - @Override - public OfficeStamperConfiguration setLineBreakPlaceholder(String lineBreakPlaceholder) { - return null; - } - - @Override - public EvaluationContextConfigurer getEvaluationContextConfigurer() { - return null; - } - - @Override - public OfficeStamperConfiguration setEvaluationContextConfigurer( - EvaluationContextConfigurer evaluationContextConfigurer - ) { - return null; - } - - @Override - public SpelParserConfiguration getSpelParserConfiguration() { - return null; - } - - @Override - public OfficeStamperConfiguration setSpelParserConfiguration( - SpelParserConfiguration spelParserConfiguration - ) { - return null; - } - - @Override - public Map, Object> getExpressionFunctions() { - return null; - } - - @Override - public Map, ITypeResolver> getTypeResolvers() { - return null; - } - - @Override - public ITypeResolver getDefaultTypeResolver() { - return null; - } - - @Override - public OfficeStamperConfiguration setDefaultTypeResolver( - ITypeResolver defaultResolver - ) { - return null; - } - - @Override - public Map, Function> getCommentProcessors() { - return null; - } - - @Override - public boolean isReplaceNullValues() { - return false; - } - - @Override - public String getNullValuesDefault() { - return null; - } - - @Override - public List getPreprocessors() { - return null; - } - - @Override - public List getResolvers() { - return null; - } - - @Override - public OfficeStamperConfiguration setResolvers(List resolvers) { - return null; - } + /** + * Creates a new standard OfficeStamperConfiguration. + * + * @return the standard OfficeStamperConfiguration + */ + public static OfficeStamperConfiguration standard() { + return new DocxStamperConfiguration(); + } - @Override - public OfficeStamperConfiguration addResolver(ObjectResolver resolver) { - return null; - } - }; + /** + * Returns a new instance of OfficeStamperConfiguration specifically for PowerPoint files. + * + * @return a new OfficeStamperConfiguration instance for PowerPoint files + */ + public static OfficeStamperConfiguration powerpoint() { + return new PowerpointStamperConfiguration(); } + } diff --git a/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java b/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java index cc45e359..597a9129 100644 --- a/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java +++ b/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java @@ -23,6 +23,13 @@ */ public class OfficeStampers { + /** + * Creates a new instance of the {@link DocxStamper} class with the specified {@link OfficeStamperConfiguration}. + * + * @param config the configuration for the docx stamper + * + * @return a new instance of the {@link DocxStamper} class + */ public static OfficeStamper docxStamper( OfficeStamperConfiguration config ) { @@ -39,10 +46,25 @@ public static OfficeStamper docxStamper() { return new DocxStamper<>(OfficeStamperConfigurations.standardWithPreprocessing()); } + /** + * Returns a new instance of the OfficeStamper implementation + * for stamping Powerpoint presentations with context and writing + * the result to an OutputStream. + * + * @return a new OfficeStamper instance for Powerpoint presentations + * @since 1.6.8 + */ public static OfficeStamper pptxStamper() { return new PowerpointStamper(); } + /** + * Returns a new instance of the OfficeStamper implementation + * for stamping Excel templates with context and writing the result to an OutputStream. + * + * @return a new OfficeStamper instance for Excel templates + * @since 1.6.8 + */ public static OfficeStamper xlsxStamper() { return new ExcelStamper(); } diff --git a/src/main/java/pro/verron/docxstamper/preset/PowerpointStamperConfiguration.java b/src/main/java/pro/verron/docxstamper/preset/PowerpointStamperConfiguration.java new file mode 100644 index 00000000..5fd30fa3 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/preset/PowerpointStamperConfiguration.java @@ -0,0 +1,201 @@ +package pro.verron.docxstamper.preset; + +import org.docx4j.openpackaging.packages.WordprocessingMLPackage; +import org.springframework.expression.spel.SpelParserConfiguration; +import pro.verron.docxstamper.api.*; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + +final class PowerpointStamperConfiguration + implements OfficeStamperConfiguration { + @Override + public Optional nullReplacementValue() { + return Optional.empty(); + } + + @Override + public boolean isFailOnUnresolvedExpression() { + return false; + } + + @Override + public OfficeStamperConfiguration setFailOnUnresolvedExpression( + boolean failOnUnresolvedExpression + ) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public OfficeStamperConfiguration nullValuesDefault(String nullValuesDefault) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public OfficeStamperConfiguration replaceNullValues(boolean replaceNullValues) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public OfficeStamperConfiguration unresolvedExpressionsDefaultValue( + String unresolvedExpressionsDefaultValue + ) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public OfficeStamperConfiguration replaceUnresolvedExpressions( + boolean replaceUnresolvedExpressions + ) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public OfficeStamperConfiguration leaveEmptyOnExpressionError( + boolean leaveEmpty + ) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public OfficeStamperConfiguration addTypeResolver( + Class resolvedType, + ITypeResolver resolver + ) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public OfficeStamperConfiguration exposeInterfaceToExpressionLanguage( + Class interfaceClass, + Object implementation + ) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public OfficeStamperConfiguration addCommentProcessor( + Class interfaceClass, + Function commentProcessorFactory + ) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public OfficeStamper build() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public void addPreprocessor(PreProcessor preprocessor) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public boolean isReplaceUnresolvedExpressions() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public boolean isLeaveEmptyOnExpressionError() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public String getUnresolvedExpressionsDefaultValue() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public String getLineBreakPlaceholder() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public OfficeStamperConfiguration setLineBreakPlaceholder(String lineBreakPlaceholder) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public EvaluationContextConfigurer getEvaluationContextConfigurer() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public OfficeStamperConfiguration setEvaluationContextConfigurer( + EvaluationContextConfigurer evaluationContextConfigurer + ) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public SpelParserConfiguration getSpelParserConfiguration() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public OfficeStamperConfiguration setSpelParserConfiguration( + SpelParserConfiguration spelParserConfiguration + ) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public Map, Object> getExpressionFunctions() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public Map, ITypeResolver> getTypeResolvers() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public ITypeResolver getDefaultTypeResolver() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public OfficeStamperConfiguration setDefaultTypeResolver( + ITypeResolver defaultResolver + ) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public Map, Function> getCommentProcessors() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public boolean isReplaceNullValues() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public String getNullValuesDefault() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public List getPreprocessors() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public List getResolvers() { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public OfficeStamperConfiguration setResolvers(List resolvers) { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public OfficeStamperConfiguration addResolver(ObjectResolver resolver) { + throw new UnsupportedOperationException("Not supported."); + } +} From 6d00de1bd612b5d32ed633fd798ad96946055204 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Tue, 30 Apr 2024 09:16:14 +0800 Subject: [PATCH 074/134] Add package-info file for docxstamper preset package This commit introduces a new package-info.java file specifically for the docxstamper preset package. This file provides presets and recommended configurations for library users. It is intended as a bootstrap, not a base for extensions. --- .../java/pro/verron/docxstamper/preset/package-info.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/pro/verron/docxstamper/preset/package-info.java diff --git a/src/main/java/pro/verron/docxstamper/preset/package-info.java b/src/main/java/pro/verron/docxstamper/preset/package-info.java new file mode 100644 index 00000000..2860c86c --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/preset/package-info.java @@ -0,0 +1,9 @@ +/** + * This package provides presets and recommended configurations for docxstamper library users; + *

+ * It should not be extended upon, put it can be used as a bootstrap for projects. + */ +@NonNullApi +package pro.verron.docxstamper.preset; + +import org.springframework.lang.NonNullApi; From a18233b50a6529b12a71c9ed531beec6118b6ecf Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Tue, 30 Apr 2024 09:18:45 +0800 Subject: [PATCH 075/134] Add JavaDoc comments and minor code improvements Extensive JavaDoc comments have been added across classes to provide clear context and function definitions. Minor code formatting changes were also made to improve code readability. A utility class was also updated to prevent instantiation. --- .../verron/docxstamper/core/CommentUtil.java | 3 ++ .../docxstamper/core/ExcelParagraph.java | 3 ++ .../verron/docxstamper/core/ExcelStamper.java | 3 ++ .../docxstamper/core/PlaceholderReplacer.java | 28 +++++++++++-------- .../docxstamper/core/PowerpointCollector.java | 24 ++++++++++++++++ .../docxstamper/core/PowerpointStamper.java | 5 ++++ .../docxstamper/core/PowerpointVisitor.java | 2 +- .../verron/docxstamper/core/package-info.java | 5 ++++ 8 files changed, 61 insertions(+), 12 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java index 30257419..6d683afb 100644 --- a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java +++ b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java @@ -35,6 +35,9 @@ public class CommentUtil { private static final Logger logger = LoggerFactory.getLogger(CommentUtil.class); private static final String WORD_COMMENTS_PART_NAME = "/word/comments.xml"; + /** + * Utility class for handling comments in a DOCX document. + */ // TODO: Move to private for next version protected CommentUtil() { throw new DocxStamperException("Utility class shouldn't be instantiated"); diff --git a/src/main/java/pro/verron/docxstamper/core/ExcelParagraph.java b/src/main/java/pro/verron/docxstamper/core/ExcelParagraph.java index 2f9b9d99..b5ca0f0d 100644 --- a/src/main/java/pro/verron/docxstamper/core/ExcelParagraph.java +++ b/src/main/java/pro/verron/docxstamper/core/ExcelParagraph.java @@ -3,6 +3,9 @@ import org.xlsx4j.sml.CTRst; import pro.verron.docxstamper.api.Placeholder; +/** + * TODO: javadoc + */ public class ExcelParagraph { private final CTRst paragraph; diff --git a/src/main/java/pro/verron/docxstamper/core/ExcelStamper.java b/src/main/java/pro/verron/docxstamper/core/ExcelStamper.java index bc80e168..361e43a8 100644 --- a/src/main/java/pro/verron/docxstamper/core/ExcelStamper.java +++ b/src/main/java/pro/verron/docxstamper/core/ExcelStamper.java @@ -13,6 +13,9 @@ import static pro.verron.docxstamper.core.Placeholders.findVariables; +/** + * TODO: javadoc + */ public class ExcelStamper implements OfficeStamper { diff --git a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java index 0a24464c..85c57ef2 100644 --- a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java +++ b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java @@ -27,8 +27,9 @@ */ public class PlaceholderReplacer implements ParagraphPlaceholderReplacer { - private static final Logger log = LoggerFactory.getLogger( - PlaceholderReplacer.class); + + private static final Logger log = LoggerFactory.getLogger(PlaceholderReplacer.class); + private final ExpressionResolver resolver; private final ObjectResolverRegistry registry; private final boolean failOnUnresolvedExpression; @@ -51,7 +52,7 @@ public class PlaceholderReplacer * that cannot be resolved will * be by replaced by an * empty string. - * @param linebreakPlaceholder if set to a non-null value, + * @param linebreakPlaceholder if set to a non-null value, * all occurrences of this placeholder will be * replaced with a line break. */ @@ -113,24 +114,28 @@ public void resolveExpressionsForParagraph( try { var resolution = resolver.resolve(expression, context); var replacement = registry.resolve(document, expression, - resolution); + resolution); paragraph.replace(expression, replacement); } catch (SpelEvaluationException | SpelParseException e) { if (failOnUnresolvedExpression) { String message = "Expression %s could not be resolved against context of type %s" .formatted(expression, context.getClass()); throw new OfficeStamperException(message, e); - } else if (leaveEmptyOnExpressionError) { + } + else if (leaveEmptyOnExpressionError) { log.warn( - "Expression {} could not be resolved against context root of type {}. Reason: {}. Set log level to TRACE to view Stacktrace.", + "Expression {} could not be resolved against context root of type {}. Reason: {}. Set log" + + " level to TRACE to view Stacktrace.", expression, context.getClass(), e.getMessage()); log.trace("Reason for skipping expression:", e); paragraph.replace(expression, RunUtil.create("")); - } else if (replaceUnresolvedExpressions) { + } + else if (replaceUnresolvedExpressions) { log.warn( - "Expression {} could not be resolved against context root of type {}. Reason: {}. Set log level to TRACE to view Stacktrace.", + "Expression {} could not be resolved against context root of type {}. Reason: {}. Set log" + + " level to TRACE to view Stacktrace.", expression, context.getClass(), e.getMessage()); @@ -138,7 +143,8 @@ public void resolveExpressionsForParagraph( paragraph.replace( expression, RunUtil.create(unresolvedExpressionsDefaultValue)); - } else { + } + else { // DO NOTHING } } @@ -156,10 +162,10 @@ private Placeholder lineBreakPlaceholder() { private void replaceLineBreaks(Paragraph paragraph) { Br lineBreak = Context.getWmlObjectFactory() - .createBr(); + .createBr(); R run = RunUtil.create(lineBreak); while (paragraph.asString() - .contains(lineBreakPlaceholder().expression())) { + .contains(lineBreakPlaceholder().expression())) { paragraph.replace(lineBreakPlaceholder(), run); } } diff --git a/src/main/java/pro/verron/docxstamper/core/PowerpointCollector.java b/src/main/java/pro/verron/docxstamper/core/PowerpointCollector.java index e01f8e29..add76da7 100644 --- a/src/main/java/pro/verron/docxstamper/core/PowerpointCollector.java +++ b/src/main/java/pro/verron/docxstamper/core/PowerpointCollector.java @@ -4,15 +4,34 @@ import java.util.ArrayList; import java.util.List; +/** + * The PowerpointCollector class is used to collect instances of a specific class in a PowerPoint presentation. + * + * @param the type of instances to collect + */ public class PowerpointCollector extends PowerpointVisitor { private final Class aClass; private final List list = new ArrayList<>(); + /** + * The PowerpointCollector class is used to collect instances of a specific class in a PowerPoint presentation. + * + * @param aClass the type of instances to collect + */ public PowerpointCollector(Class aClass) { this.aClass = aClass; } + /** + * Collects instances of a specific class in a PowerPoint presentation. + * + * @param the type of instances to collect + * @param template the PowerPoint presentation template + * @param aClass the type of instances to collect + * + * @return a list of instances of the specified class + */ public static List collect( Object template, Class aClass @@ -22,6 +41,11 @@ public static List collect( return collector.collect(); } + /** + * Retrieves the collected instances of a specific class. + * + * @return an instance list of the specified class + */ public List collect() { return list; } diff --git a/src/main/java/pro/verron/docxstamper/core/PowerpointStamper.java b/src/main/java/pro/verron/docxstamper/core/PowerpointStamper.java index 22eefb37..4587055f 100644 --- a/src/main/java/pro/verron/docxstamper/core/PowerpointStamper.java +++ b/src/main/java/pro/verron/docxstamper/core/PowerpointStamper.java @@ -13,6 +13,11 @@ import java.io.OutputStream; import java.util.List; +/** + * The PowerpointStamper class implements the OfficeStamper interface + * to provide functionality for stamping Powerpoint presentations with + * context and writing the result to an OutputStream. + */ public class PowerpointStamper implements OfficeStamper { diff --git a/src/main/java/pro/verron/docxstamper/core/PowerpointVisitor.java b/src/main/java/pro/verron/docxstamper/core/PowerpointVisitor.java index de27d63f..9c32d0d6 100644 --- a/src/main/java/pro/verron/docxstamper/core/PowerpointVisitor.java +++ b/src/main/java/pro/verron/docxstamper/core/PowerpointVisitor.java @@ -20,7 +20,6 @@ public final void visit(Object object) { before(object); try { if (object instanceof PresentationMLPackage element) visit(element.getParts()); - else if (object instanceof PartName ignored) { /* Do nothing */ } else if (object instanceof Parts element) visit(element.getParts()); else if (object instanceof SlideLayoutPart ignored) { /* Do nothing */ } @@ -47,6 +46,7 @@ else if (object instanceof CTRegularTextRun ignored) { /* Do nothing */ } else if (object instanceof Presentation.SldSz ignored) { /* Do Nothing */ } else if (object instanceof Presentation ignored) { /* Do Nothing */ } else if (object == null) { /* Do Nothing */ } + // TODO: replace this test throws by a noop before usage in the wild else throw new OfficeStamperException("Unknown case %s : %s".formatted(object.getClass(), object)); } catch (Docx4JException e) { throw new OfficeStamperException(e); diff --git a/src/main/java/pro/verron/docxstamper/core/package-info.java b/src/main/java/pro/verron/docxstamper/core/package-info.java index c2a02d7e..7119a709 100644 --- a/src/main/java/pro/verron/docxstamper/core/package-info.java +++ b/src/main/java/pro/verron/docxstamper/core/package-info.java @@ -1,3 +1,8 @@ +/** + * This package provides the core functionality for docxstamper. + *

+ * It should not be depended on by third-party. + */ @NonNullApi package pro.verron.docxstamper.core; From b62fe4bff7519e38d094ae0927da4081a4fb362c Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Tue, 30 Apr 2024 09:19:19 +0800 Subject: [PATCH 076/134] Refactor CommentUtil getCommentAround method This commit removes the null check on the run parameter in the getCommentAround method. The responsibility to check if run is null is likely shifted to the calling functions. The refactoring leads to cleaner and more concise code. --- src/main/java/pro/verron/docxstamper/core/CommentUtil.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java index 6d683afb..f4dc98dd 100644 --- a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java +++ b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java @@ -53,8 +53,6 @@ protected CommentUtil() { public static Optional getCommentAround( R run, WordprocessingMLPackage document ) { - if (run == null) return Optional.empty(); - ContentAccessor parent = (ContentAccessor) ((Child) run).getParent(); if (parent == null) return Optional.empty(); From 5c3a7d5d38c8a465ff705e7265db6652ea6bc1bb Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Tue, 30 Apr 2024 09:19:37 +0800 Subject: [PATCH 077/134] Refactor PowerpointParagraph to improve type safety The 'replace' method in the PowerpointParagraph class has been refactored to improve type safety. Instead of expecting a 'CTRegularTextRun' object as input, the method now takes a generic 'Object'. It also includes a type check to verify the replacement object is of type 'CTRegularTextRun', ensuring stronger type safety. --- .../verron/docxstamper/core/PowerpointParagraph.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/core/PowerpointParagraph.java b/src/main/java/pro/verron/docxstamper/core/PowerpointParagraph.java index 18250435..70e39983 100644 --- a/src/main/java/pro/verron/docxstamper/core/PowerpointParagraph.java +++ b/src/main/java/pro/verron/docxstamper/core/PowerpointParagraph.java @@ -25,7 +25,7 @@ * @since 1.0.8 */ public class PowerpointParagraph - implements Paragraph { + implements Paragraph { private final List runs = new ArrayList<>(); private final CTTextParagraph paragraph; private int currentPosition = 0; @@ -79,7 +79,9 @@ private void addRun(CTRegularTextRun run, int index) { * @param replacement the object to replace the expression. */ @Override - public void replace(Placeholder placeholder, CTRegularTextRun replacement) { + public void replace(Placeholder placeholder, Object replacement) { + if (!(replacement instanceof CTRegularTextRun replacementRun)) + throw new AssertionError("replacement is not a CTRegularTextRun"); String text = asString(); String full = placeholder.expression(); int matchStartIndex = text.indexOf(full); @@ -106,7 +108,7 @@ public void replace(Placeholder placeholder, CTRegularTextRun replacement) { boolean expressionAtEndOfRun = matchEndIndex == run.endIndex(); boolean expressionWithinRun = matchStartIndex > run.startIndex() && matchEndIndex < run.endIndex(); - replacement.setRPr(run.run() + replacementRun.setRPr(run.run() .getRPr()); if (expressionSpansCompleteRun) { @@ -143,7 +145,7 @@ else if (expressionWithinRun) { else { PowerpointRun firstRun = affectedRuns.get(0); PowerpointRun lastRun = affectedRuns.get(affectedRuns.size() - 1); - replacement.setRPr(firstRun.run() + replacementRun.setRPr(firstRun.run() .getRPr()); // remove the expression from first and last run firstRun.replace(matchStartIndex, matchEndIndex, ""); From 2204fbc44d6925f594bd2b3593f8c9737e381be3 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Tue, 30 Apr 2024 09:19:54 +0800 Subject: [PATCH 078/134] Refactor PowerpointRun and add comments The PowerpointRun class has been refactored for better readability. Detailed comments have been added to explain the purpose and functionality of the class and its methods. In particular, in-depth descriptions have been provided for the isTouchedByRange method, and the replace method. A new private method lastIndex has also been introduced to reduce code duplication. --- .../docxstamper/core/PowerpointRun.java | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/core/PowerpointRun.java b/src/main/java/pro/verron/docxstamper/core/PowerpointRun.java index fdf4879e..153cbe83 100644 --- a/src/main/java/pro/verron/docxstamper/core/PowerpointRun.java +++ b/src/main/java/pro/verron/docxstamper/core/PowerpointRun.java @@ -2,13 +2,36 @@ import org.docx4j.dml.CTRegularTextRun; -public record PowerpointRun(int startIndex, int endIndex, int indexInParent, CTRegularTextRun run) { +/** + * Represents a run within a PowerPoint slide. + */ +public record PowerpointRun( + int startIndex, + int endIndex, + int indexInParent, + CTRegularTextRun run +) { + /** + * Checks if the given range of indices touches the start or end index of the run. + * + * @param globalStartIndex the start index of the global range. + * @param globalEndIndex the end index of the global range. + * + * @return {@code true} if the range touches the start or end index of the run, {@code false} otherwise. + */ public boolean isTouchedByRange(int globalStartIndex, int globalEndIndex) { return ((startIndex >= globalStartIndex) && (startIndex <= globalEndIndex)) || ((endIndex >= globalStartIndex) && (endIndex <= globalEndIndex)) || ((startIndex <= globalStartIndex) && (endIndex >= globalEndIndex)); } + /** + * Replaces a substring within the run's text. + * + * @param globalStartIndex the start index of the substring to be replaced. + * @param globalEndIndex the end index of the substring to be replaced. + * @param replacement the replacement string. + */ public void replace( int globalStartIndex, int globalEndIndex, @@ -25,10 +48,14 @@ public void replace( private int globalIndexToLocalIndex(int globalIndex) { if (globalIndex < startIndex) return 0; - else if (globalIndex > endIndex) return lastIndex(run.getT()); + else if (globalIndex > endIndex) return lastIndex(); else return globalIndex - startIndex; } + private int lastIndex() { + return lastIndex(run.getT()); + } + private int lastIndex(String string) { return string.length() - 1; } From 5c309df2c9c4195d5229cb0a635a17d5e508feaa Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Tue, 30 Apr 2024 09:20:39 +0800 Subject: [PATCH 079/134] Refactor StandardParagraph to enforce R class replacements The changes are primarily focused on enforcing the replacement parameter to be of class R in the replace method of StandardParagraph. The code also removes the unused 'getRuns' method. The validation is implemented by throwing an AssertionError if the replacement is not an instance of R. This change enforces the stringent requirement while simplifying the overall structure of the code. --- .../docxstamper/core/StandardParagraph.java | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/core/StandardParagraph.java b/src/main/java/pro/verron/docxstamper/core/StandardParagraph.java index 876b362c..e75c8bd8 100644 --- a/src/main/java/pro/verron/docxstamper/core/StandardParagraph.java +++ b/src/main/java/pro/verron/docxstamper/core/StandardParagraph.java @@ -27,7 +27,7 @@ * @since 1.0.8 */ public class StandardParagraph - implements Paragraph { + implements Paragraph { private final List runs = new ArrayList<>(); private final P paragraph; private int currentPosition = 0; @@ -81,7 +81,10 @@ private void addRun(R run, int index) { * @param replacement the object to replace the expression. */ @Override - public void replace(Placeholder placeholder, R replacement) { + public void replace(Placeholder placeholder, Object replacement) { + if (!(replacement instanceof R replacementRun)) + throw new AssertionError("replacement must be a R"); + String text = asString(); String full = placeholder.expression(); int matchStartIndex = text.indexOf(full); @@ -106,7 +109,7 @@ public void replace(Placeholder placeholder, R replacement) { boolean expressionAtEndOfRun = matchEndIndex == run.endIndex(); boolean expressionWithinRun = matchStartIndex > run.startIndex() && matchEndIndex < run.endIndex(); - replacement.setRPr(run.run() + replacementRun.setRPr(run.run() .getRPr()); if (expressionSpansCompleteRun) { @@ -146,7 +149,7 @@ public void replace(Placeholder placeholder, R replacement) { } else { IndexedRun firstRun = affectedRuns.get(0); IndexedRun lastRun = affectedRuns.get(affectedRuns.size() - 1); - replacement.setRPr(firstRun.run() + replacementRun.setRPr(firstRun.run() .getRPr()); // remove the expression from first and last run firstRun.replace(matchStartIndex, matchEndIndex, ""); @@ -188,18 +191,6 @@ private List getAffectedRuns(int startIndex, int endIndex) { .toList(); } - /** - * Returns the list of runs that are aggregated. Depending on what modifications were done to the aggregated text, - * this list may not return the same runs initially added to the aggregator. - * - * @return the list of aggregated runs. - */ - private List getRuns() { - return runs.stream() - .map(IndexedRun::run) - .toList(); - } - /** * {@inheritDoc} */ From 53b8001e77f51471c9ba22c8a9795911e1dc259c Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 2 May 2024 06:30:18 +0800 Subject: [PATCH 080/134] Refactor ParagraphRepeatProcessor by deprecating old method The method newInstance(PlaceholderReplacer, String) is now deprecated in the ParagraphRepeatProcessor class. The new preferred method for instantiation is via newInstance(ParagraphPlaceholderReplacer) and utilizing addResolver(ObjectResolver) with nullToDefault(String) for null value replacements from OfficeStamperConfiguration and Resolvers classes, respectively. Also made minor code formatting updates for better readability. --- .../repeat/ParagraphRepeatProcessor.java | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java index 8d0a7166..3659f282 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java @@ -7,12 +7,11 @@ import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.util.ParagraphUtil; import org.wickedsource.docxstamper.util.SectionUtil; -import pro.verron.docxstamper.api.Comment; -import pro.verron.docxstamper.api.CommentProcessor; -import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; +import pro.verron.docxstamper.api.*; import pro.verron.docxstamper.core.CommentUtil; import pro.verron.docxstamper.core.PlaceholderReplacer; import pro.verron.docxstamper.core.StandardParagraph; +import pro.verron.docxstamper.preset.Resolvers; import java.math.BigInteger; import java.util.*; @@ -54,35 +53,43 @@ private ParagraphRepeatProcessor( * * @param pr replaces expressions with values * @param nullReplacement replaces null values + * * @return a new instance of ParagraphRepeatProcessor + * + * @deprecated use {@link ParagraphRepeatProcessor#newInstance(ParagraphPlaceholderReplacer)} for instantiation + * and {@link OfficeStamperConfiguration#addResolver(ObjectResolver)} with + * {@link Resolvers#nullToDefault(String)} instead */ - // TODO: remove ? + @Deprecated(since = "1.6.8", forRemoval = true) public static CommentProcessor newInstance( PlaceholderReplacer pr, String nullReplacement ) { return new ParagraphRepeatProcessor(pr, - () -> singletonList(ParagraphUtil.create( - nullReplacement))); + () -> singletonList(ParagraphUtil.create( + nullReplacement))); } /** *

newInstance.

* * @param placeholderReplacer replaces expressions with values + * * @return a new instance of ParagraphRepeatProcessor */ public static CommentProcessor newInstance(ParagraphPlaceholderReplacer placeholderReplacer) { return new ParagraphRepeatProcessor(placeholderReplacer, - Collections::emptyList); + Collections::emptyList); } + /** * Returns all paragraphs inside the comment of the given paragraph. *

* If the paragraph is not inside a comment, the given paragraph is returned. * * @param paragraph the paragraph to analyze + * * @return all paragraphs inside the comment of the given paragraph */ public static Deque

getParagraphsInsideComment(P paragraph) { @@ -104,23 +111,24 @@ public static Deque

getParagraphsInsideComment(P paragraph) { Object parent = paragraph.getParent(); if (parent instanceof ContentAccessor contentAccessor) { int index = contentAccessor.getContent() - .indexOf(paragraph); + .indexOf(paragraph); for (int i = index + 1; i < contentAccessor.getContent() - .size() && !foundEnd; i++) { + .size() && !foundEnd; i++) { Object next = contentAccessor.getContent() - .get(i); + .get(i); if (next instanceof CommentRangeEnd cre && cre.getId() - .equals(commentId)) { + .equals(commentId)) { foundEnd = true; - } else { + } + else { if (next instanceof P p) { paragraphs.add(p); } if (next instanceof ContentAccessor childContent) { for (Object child : childContent.getContent()) { if (child instanceof CommentRangeEnd cre && cre.getId() - .equals(commentId)) { + .equals(commentId)) { foundEnd = true; break; } @@ -142,7 +150,6 @@ private static void restoreFirstSectionBreakIfNeeded( breakP); } } - /** * {@inheritDoc} */ @@ -170,7 +177,6 @@ public void repeatParagraph(List objects) { paragraph.getPPr() .setSectPr(null); } - pToRepeat.put(paragraph, toRepeat); } @@ -189,17 +195,17 @@ private Deque

generateParagraphsToAdd( P pClone = XmlUtils.deepCopy(paragraphToClone); if (paragraphs.sectionBreakBefore != null - && paragraphs.hasOddSectionBreaks - && expressionContext != lastExpressionContext - && paragraphToClone == lastParagraph + && paragraphs.hasOddSectionBreaks + && expressionContext != lastExpressionContext + && paragraphToClone == lastParagraph ) { SectionUtil.applySectionBreakToParagraph(paragraphs.sectionBreakBefore, - pClone); + pClone); } CommentUtil.deleteCommentFromElement(pClone.getContent(), - paragraphs.comment.getComment() - .getId()); + paragraphs.comment.getComment() + .getId()); placeholderReplacer.resolveExpressionsForParagraph( new StandardParagraph(pClone), expressionContext, From 19e281294f89cb56915c7bd5525a9031f7a367f4 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 2 May 2024 06:31:58 +0800 Subject: [PATCH 081/134] Refactor repeatParagraph and commitChanges methods in ParagraphRepeatProcessor Rearranged the methods within the ParagraphRepeatProcessor class. Previously, the method repeatParagraph was called before the private method restoreFirstSectionBreakIfNeeded. However, this private method has been moved to be called within the commitChanges method instead. Also added more detail to the method comment for the repeatParagraph method. --- .../repeat/ParagraphRepeatProcessor.java | 110 +++++++++--------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java index 3659f282..f66f4d76 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java @@ -82,6 +82,35 @@ public static CommentProcessor newInstance(ParagraphPlaceholderReplacer placehol Collections::emptyList); } + /** + * {@inheritDoc} + */ + @Override + public void repeatParagraph(List objects) { + P paragraph = getParagraph(); + + Deque

paragraphs = getParagraphsInsideComment(paragraph); + + Paragraphs toRepeat = new Paragraphs(); + toRepeat.comment = getCurrentCommentWrapper(); + toRepeat.data = new ArrayDeque<>(objects); + toRepeat.paragraphs = paragraphs; + toRepeat.sectionBreakBefore = SectionUtil.getPreviousSectionBreakIfPresent( + paragraph, + (ContentAccessor) paragraph.getParent()); + toRepeat.firstParagraphSectionBreak = SectionUtil.getParagraphSectionBreak( + paragraph); + toRepeat.hasOddSectionBreaks = SectionUtil.isOddNumberOfSectionBreaks( + new ArrayList<>(toRepeat.paragraphs)); + + if (paragraph.getPPr() != null && paragraph.getPPr() + .getSectPr() != null) { + // we need to clear the first paragraph's section break to be able to control how to repeat it + paragraph.getPPr() + .setSectPr(null); + } + pToRepeat.put(paragraph, toRepeat); + } /** * Returns all paragraphs inside the comment of the given paragraph. @@ -140,44 +169,30 @@ public static Deque

getParagraphsInsideComment(P paragraph) { return paragraphs; } - private static void restoreFirstSectionBreakIfNeeded( - Paragraphs paragraphs, - Deque

paragraphsToAdd - ) { - if (paragraphs.firstParagraphSectionBreak != null) { - P breakP = paragraphsToAdd.getLast(); - SectionUtil.applySectionBreakToParagraph(paragraphs.firstParagraphSectionBreak, - breakP); - } - } /** * {@inheritDoc} */ @Override - public void repeatParagraph(List objects) { - P paragraph = getParagraph(); - - Deque

paragraphs = getParagraphsInsideComment(paragraph); - - Paragraphs toRepeat = new Paragraphs(); - toRepeat.comment = getCurrentCommentWrapper(); - toRepeat.data = new ArrayDeque<>(objects); - toRepeat.paragraphs = paragraphs; - toRepeat.sectionBreakBefore = SectionUtil.getPreviousSectionBreakIfPresent( - paragraph, - (ContentAccessor) paragraph.getParent()); - toRepeat.firstParagraphSectionBreak = SectionUtil.getParagraphSectionBreak( - paragraph); - toRepeat.hasOddSectionBreaks = SectionUtil.isOddNumberOfSectionBreaks( - new ArrayList<>(toRepeat.paragraphs)); + public void commitChanges(WordprocessingMLPackage document) { + for (Map.Entry entry : pToRepeat.entrySet()) { + P currentP = entry.getKey(); + ContentAccessor parent = (ContentAccessor) currentP.getParent(); + List parentContent = parent.getContent(); + int index = parentContent.indexOf(currentP); + if (index < 0) throw new DocxStamperException("Impossible"); - if (paragraph.getPPr() != null && paragraph.getPPr() - .getSectPr() != null) { - // we need to clear the first paragraph's section break to be able to control how to repeat it - paragraph.getPPr() - .setSectPr(null); + Paragraphs paragraphsToRepeat = entry.getValue(); + Deque expressionContexts = Objects.requireNonNull( + paragraphsToRepeat).data; + Deque

collection = expressionContexts == null + ? new ArrayDeque<>(nullSupplier.get()) + : generateParagraphsToAdd(document, + paragraphsToRepeat, + expressionContexts); + restoreFirstSectionBreakIfNeeded(paragraphsToRepeat, collection); + parentContent.addAll(index, collection); + parentContent.removeAll(paragraphsToRepeat.paragraphs); } - pToRepeat.put(paragraph, toRepeat); } private Deque

generateParagraphsToAdd( @@ -217,29 +232,14 @@ private Deque

generateParagraphsToAdd( return paragraphsToAdd; } - /** - * {@inheritDoc} - */ - @Override - public void commitChanges(WordprocessingMLPackage document) { - for (Map.Entry entry : pToRepeat.entrySet()) { - P currentP = entry.getKey(); - ContentAccessor parent = (ContentAccessor) currentP.getParent(); - List parentContent = parent.getContent(); - int index = parentContent.indexOf(currentP); - if (index < 0) throw new DocxStamperException("Impossible"); - - Paragraphs paragraphsToRepeat = entry.getValue(); - Deque expressionContexts = Objects.requireNonNull( - paragraphsToRepeat).data; - Deque

collection = expressionContexts == null - ? new ArrayDeque<>(nullSupplier.get()) - : generateParagraphsToAdd(document, - paragraphsToRepeat, - expressionContexts); - restoreFirstSectionBreakIfNeeded(paragraphsToRepeat, collection); - parentContent.addAll(index, collection); - parentContent.removeAll(paragraphsToRepeat.paragraphs); + private static void restoreFirstSectionBreakIfNeeded( + Paragraphs paragraphs, + Deque

paragraphsToAdd + ) { + if (paragraphs.firstParagraphSectionBreak != null) { + P breakP = paragraphsToAdd.getLast(); + SectionUtil.applySectionBreakToParagraph(paragraphs.firstParagraphSectionBreak, + breakP); } } From a360481efa21aa50426eb02ed66994c14eaf7b32 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 2 May 2024 06:32:47 +0800 Subject: [PATCH 082/134] Refactor RepeatDocPartProcessor class for readability This commit cleans up and improves the Readability in RepeatDocPartProcessor class. It mainly refactors the codes by replacing explicit types with var keywords and adjusts the indentations. Moreover, some unnecessary white spaces and lines are also removed to make the code more concise. --- .../repeat/RepeatDocPartProcessor.java | 101 +++++++++--------- 1 file changed, 48 insertions(+), 53 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java index 1b83a2fa..828168bf 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java @@ -153,7 +153,6 @@ private static void setParentIfPossible( if (object instanceof Child child) child.setParent(parent); } - /** * {@inheritDoc} */ @@ -170,56 +169,6 @@ public void repeatDocPart(List contexts) { } } - private List stampSubDocuments( - WordprocessingMLPackage document, - List expressionContexts, - ContentAccessor gcp, - WordprocessingMLPackage subTemplate, - SectPr previousSectionBreak, - boolean oddNumberOfBreaks - ) { - Deque subDocuments = stampSubDocuments( - expressionContexts, subTemplate); - Map replacements = subDocuments - .stream() - .map(p -> walkObjectsAndImportImages(p, - document)) // TODO: remove the side effect here - .map(Map::entrySet) - .flatMap(Set::stream) - .collect(toMap(Entry::getKey, Entry::getValue)); - - var changes = new ArrayList<>(); - for (WordprocessingMLPackage subDocument : subDocuments) { - var os = documentAsInsertableElements(subDocument, - oddNumberOfBreaks, - previousSectionBreak); - os.stream() - .filter(ContentAccessor.class::isInstance) - .map(ContentAccessor.class::cast) - .forEach(o -> recursivelyReplaceImages(o, replacements)); - os.forEach(c -> setParentIfPossible(c, gcp)); - changes.addAll(os); - } - return changes; - } - - private Deque stampSubDocuments( - List subContexts, - WordprocessingMLPackage subTemplate - ) { - Deque subDocuments = new ArrayDeque<>(); - for (Object subContext : subContexts) { - WordprocessingMLPackage templateCopy = outputWord( - os -> copy(subTemplate, os)); - WordprocessingMLPackage subDocument = outputWord( - os -> stamp(subContext, - templateCopy, - os - )); - subDocuments.add(subDocument); - } - return subDocuments; - } /** * {@inheritDoc} @@ -253,6 +202,51 @@ public void commitChanges(WordprocessingMLPackage document) { gcpContent.addAll(index, changes); gcpContent.removeAll(repeatElements); } + + } + + private List stampSubDocuments( + WordprocessingMLPackage document, + List expressionContexts, + ContentAccessor gcp, + WordprocessingMLPackage subTemplate, + SectPr previousSectionBreak, + boolean oddNumberOfBreaks + ) { + var subDocuments = stampSubDocuments(expressionContexts, subTemplate); + var replacements = subDocuments + .stream() + .map(p -> walkObjectsAndImportImages(p, document)) // TODO_LATER: move the side effect somewhere else + .map(Map::entrySet) + .flatMap(Set::stream) + .collect(toMap(Entry::getKey, Entry::getValue)); + + var changes = new ArrayList<>(); + for (WordprocessingMLPackage subDocument : subDocuments) { + var os = documentAsInsertableElements(subDocument, + oddNumberOfBreaks, + previousSectionBreak); + os.stream() + .filter(ContentAccessor.class::isInstance) + .map(ContentAccessor.class::cast) + .forEach(o -> recursivelyReplaceImages(o, replacements)); + os.forEach(c -> setParentIfPossible(c, gcp)); + changes.addAll(os); + } + return changes; + } + + private List stampSubDocuments( + List subContexts, + WordprocessingMLPackage subTemplate + ) { + var subDocuments = new ArrayList(); + for (Object subContext : subContexts) { + var templateCopy = outputWord(os -> copy(subTemplate, os)); + var subDocument = outputWord(os -> stamp(subContext, templateCopy, os)); + subDocuments.add(subDocument); + } + return subDocuments; } private WordprocessingMLPackage outputWord(Consumer outputter) { @@ -275,7 +269,7 @@ private WordprocessingMLPackage outputWord(Consumer outputter) { } catch (Docx4JException | IOException | InterruptedException e) { DocxStamperException exception = new DocxStamperException(e); exceptionHandler.exception() - .ifPresent(exception::addSuppressed); + .ifPresent(exception::addSuppressed); throw exception; } } @@ -337,7 +331,8 @@ default void run() { * * @throws Exception if an exception occurs executing the task */ - void throwingRun() throws Exception; + void throwingRun() + throws Exception; } /** From 6b17a327d46cb2b187d8457f21e62c4d78c46943 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 2 May 2024 06:33:46 +0800 Subject: [PATCH 083/134] Refactor RepeatDocPartProcessor class Three methods - documentAsInsertableElements, recursivelyReplaceImages, and setParentIfPossible - were rearranged in the RepeatDocPartProcessor class. The rearrangement allows for improved code readability and navigability, ensuring that similar or related methods are grouped together for increased efficiency and ease of understanding. --- .../repeat/RepeatDocPartProcessor.java | 172 +++++++++--------- 1 file changed, 88 insertions(+), 84 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java index 828168bf..5c2be0d4 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java @@ -71,6 +71,7 @@ private RepeatDocPartProcessor( * @param stamper the stamper * @param nullReplacementValue the value to use when the expression * resolves to null + * * @return a new instance of this processor */ public static CommentProcessor newInstance( @@ -88,6 +89,7 @@ public static CommentProcessor newInstance( * * @param pr the placeholderReplacer * @param stamper the stamper + * * @return a new instance of this processor */ public static CommentProcessor newInstance( @@ -97,28 +99,19 @@ public static CommentProcessor newInstance( return new RepeatDocPartProcessor(pr, stamper, Collections::emptyList); } - private static void recursivelyReplaceImages( - ContentAccessor r, - Map replacements - ) { - Queue q = new ArrayDeque<>(); - q.add(r); - while (!q.isEmpty()) { - ContentAccessor run = q.remove(); - if (replacements.containsKey(run) - && run instanceof Child child - && child.getParent() instanceof ContentAccessor parent) { - List parentContent = parent.getContent(); - parentContent.add(parentContent.indexOf(run), - replacements.get(run)); - parentContent.remove(run); - } else { - q.addAll(run.getContent() - .stream() - .filter(ContentAccessor.class::isInstance) - .map(ContentAccessor.class::cast) - .toList()); - } + /** + * {@inheritDoc} + */ + @Override + public void repeatDocPart(List contexts) { + if (contexts == null) + contexts = Collections.emptyList(); + + Comment currentComment = getCurrentCommentWrapper(); + List repeatElements = currentComment.getRepeatElements(); + + if (!repeatElements.isEmpty()) { + this.contexts.put(currentComment, contexts); } } @@ -133,78 +126,20 @@ private static List documentAsInsertableElements( if (oddNumberOfBreaks && previousSectionBreak != null) { if (DocumentUtil.lastElement(subDocument) instanceof P p) { SectionUtil.applySectionBreakToParagraph(previousSectionBreak, - p); - } else { + p); + } + else { // when the last element to be repeated is not a paragraph, we need to add a new // one right after to carry the section break to have a valid xml P p = objectFactory.createP(); SectionUtil.applySectionBreakToParagraph(previousSectionBreak, - p); + p); inserts.add(p); } } return inserts; } - private static void setParentIfPossible( - Object object, - ContentAccessor parent - ) { - if (object instanceof Child child) - child.setParent(parent); - } - /** - * {@inheritDoc} - */ - @Override - public void repeatDocPart(List contexts) { - if (contexts == null) - contexts = Collections.emptyList(); - - Comment currentComment = getCurrentCommentWrapper(); - List repeatElements = currentComment.getRepeatElements(); - - if (!repeatElements.isEmpty()) { - this.contexts.put(currentComment, contexts); - } - } - - - /** - * {@inheritDoc} - */ - @Override - public void commitChanges(WordprocessingMLPackage document) { - for (Entry> entry : this.contexts.entrySet()) { - Comment comment = entry.getKey(); - List expressionContexts = entry.getValue(); - ContentAccessor gcp = Objects.requireNonNull( - comment.getParent()); - List repeatElements = comment.getRepeatElements(); - WordprocessingMLPackage subTemplate = comment.tryBuildingSubtemplate( - document); - SectPr previousSectionBreak = SectionUtil.getPreviousSectionBreakIfPresent( - repeatElements.get(0), gcp); - boolean oddNumberOfBreaks = SectionUtil.isOddNumberOfSectionBreaks( - repeatElements); - - List changes = expressionContexts == null - ? nullSupplier.get() - : stampSubDocuments(document, - expressionContexts, - gcp, - subTemplate, - previousSectionBreak, - oddNumberOfBreaks); - - List gcpContent = gcp.getContent(); - int index = gcpContent.indexOf(repeatElements.get(0)); - gcpContent.addAll(index, changes); - gcpContent.removeAll(repeatElements); - } - - } - private List stampSubDocuments( WordprocessingMLPackage document, List expressionContexts, @@ -249,6 +184,75 @@ private List stampSubDocuments( return subDocuments; } + private static void recursivelyReplaceImages( + ContentAccessor r, + Map replacements + ) { + Queue q = new ArrayDeque<>(); + q.add(r); + while (!q.isEmpty()) { + ContentAccessor run = q.remove(); + if (replacements.containsKey(run) + && run instanceof Child child + && child.getParent() instanceof ContentAccessor parent) { + List parentContent = parent.getContent(); + parentContent.add(parentContent.indexOf(run), + replacements.get(run)); + parentContent.remove(run); + } + else { + q.addAll(run.getContent() + .stream() + .filter(ContentAccessor.class::isInstance) + .map(ContentAccessor.class::cast) + .toList()); + } + } + } + + private static void setParentIfPossible( + Object object, + ContentAccessor parent + ) { + if (object instanceof Child child) + child.setParent(parent); + } + + /** + * {@inheritDoc} + */ + @Override + public void commitChanges(WordprocessingMLPackage document) { + for (Entry> entry : this.contexts.entrySet()) { + Comment comment = entry.getKey(); + List expressionContexts = entry.getValue(); + ContentAccessor gcp = Objects.requireNonNull( + comment.getParent()); + List repeatElements = comment.getRepeatElements(); + WordprocessingMLPackage subTemplate = comment.tryBuildingSubtemplate( + document); + SectPr previousSectionBreak = SectionUtil.getPreviousSectionBreakIfPresent( + repeatElements.get(0), gcp); + boolean oddNumberOfBreaks = SectionUtil.isOddNumberOfSectionBreaks( + repeatElements); + + List changes = expressionContexts == null + ? nullSupplier.get() + : stampSubDocuments(document, + expressionContexts, + gcp, + subTemplate, + previousSectionBreak, + oddNumberOfBreaks); + + List gcpContent = gcp.getContent(); + int index = gcpContent.indexOf(repeatElements.get(0)); + gcpContent.addAll(index, changes); + gcpContent.removeAll(repeatElements); + } + + } + private WordprocessingMLPackage outputWord(Consumer outputter) { var exceptionHandler = new ProcessorExceptionHandler(); try ( From 7e75924b1afae6942500af397ec630d5c966c97c Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 2 May 2024 06:36:06 +0800 Subject: [PATCH 084/134] Refactor RepeatDocPartProcessor and deprecate the old method The RepeatDocPartProcessor class is updated and the change involves adding the @Deprecated annotation to the old newInstance method and replacing the @Comment related imports with a wildcard import. The commitChanges function has been moved above the private helper methods and the documentAsInsertableElements function is moved below them to further enhance the readability of the class. --- .../repeat/RepeatDocPartProcessor.java | 79 ++++++++++--------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java index 5c2be0d4..66da5930 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java @@ -10,11 +10,9 @@ import org.wickedsource.docxstamper.util.DocumentUtil; import org.wickedsource.docxstamper.util.ParagraphUtil; import org.wickedsource.docxstamper.util.SectionUtil; -import pro.verron.docxstamper.api.Comment; -import pro.verron.docxstamper.api.CommentProcessor; -import pro.verron.docxstamper.api.OfficeStamper; -import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; +import pro.verron.docxstamper.api.*; import pro.verron.docxstamper.core.PlaceholderReplacer; +import pro.verron.docxstamper.preset.Resolvers; import java.io.IOException; import java.io.OutputStream; @@ -73,7 +71,12 @@ private RepeatDocPartProcessor( * resolves to null * * @return a new instance of this processor + * + * @deprecated use {@link RepeatDocPartProcessor#newInstance(ParagraphPlaceholderReplacer, OfficeStamper)} for + * instantiation and {@link OfficeStamperConfiguration#addResolver(ObjectResolver)} with + * {@link Resolvers#nullToDefault(String)} instead */ + @Deprecated(since = "1.6.8", forRemoval = true) public static CommentProcessor newInstance( PlaceholderReplacer pr, OfficeStamper stamper, @@ -184,40 +187,6 @@ private List stampSubDocuments( return subDocuments; } - private static void recursivelyReplaceImages( - ContentAccessor r, - Map replacements - ) { - Queue q = new ArrayDeque<>(); - q.add(r); - while (!q.isEmpty()) { - ContentAccessor run = q.remove(); - if (replacements.containsKey(run) - && run instanceof Child child - && child.getParent() instanceof ContentAccessor parent) { - List parentContent = parent.getContent(); - parentContent.add(parentContent.indexOf(run), - replacements.get(run)); - parentContent.remove(run); - } - else { - q.addAll(run.getContent() - .stream() - .filter(ContentAccessor.class::isInstance) - .map(ContentAccessor.class::cast) - .toList()); - } - } - } - - private static void setParentIfPossible( - Object object, - ContentAccessor parent - ) { - if (object instanceof Child child) - child.setParent(parent); - } - /** * {@inheritDoc} */ @@ -253,6 +222,40 @@ public void commitChanges(WordprocessingMLPackage document) { } + private static void recursivelyReplaceImages( + ContentAccessor r, + Map replacements + ) { + Queue q = new ArrayDeque<>(); + q.add(r); + while (!q.isEmpty()) { + ContentAccessor run = q.remove(); + if (replacements.containsKey(run) + && run instanceof Child child + && child.getParent() instanceof ContentAccessor parent) { + List parentContent = parent.getContent(); + parentContent.add(parentContent.indexOf(run), + replacements.get(run)); + parentContent.remove(run); + } + else { + q.addAll(run.getContent() + .stream() + .filter(ContentAccessor.class::isInstance) + .map(ContentAccessor.class::cast) + .toList()); + } + } + } + + private static void setParentIfPossible( + Object object, + ContentAccessor parent + ) { + if (object instanceof Child child) + child.setParent(parent); + } + private WordprocessingMLPackage outputWord(Consumer outputter) { var exceptionHandler = new ProcessorExceptionHandler(); try ( From ee4ac6293f6db62053ea41e61c4c9787515823eb Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 2 May 2024 07:13:00 +0800 Subject: [PATCH 085/134] Refactor method name and apply code formatting Refactored the method name from 'getRepeatElements()' to 'getElements()' as the new name does not imply a specific usage. Also, applied code formatting based on project standards in classes Comment and StandardComment. Moved TODO comments to be less obtrusive. --- .../repeat/RepeatDocPartProcessor.java | 2 +- .../docxstamper/util/DocxImageExtractor.java | 2 +- .../docxstamper/util/ObjectDeleter.java | 4 +- .../pro/verron/docxstamper/api/Comment.java | 29 +- .../pro/verron/docxstamper/api/Image.java | 4 +- .../api/OfficeStamperConfiguration.java | 2 +- .../docxstamper/core/StandardComment.java | 286 +++++++++--------- 7 files changed, 176 insertions(+), 153 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java index 66da5930..86c3f32b 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java @@ -263,7 +263,7 @@ private WordprocessingMLPackage outputWord(Consumer outputter) { PipedInputStream is = new PipedInputStream(os) ) { // closing on exception to not block the pipe infinitely - // TODO: model both PipedxxxStream as 1 class for only 1 close() + // TODO_LATER: model both PipedxxxStream as 1 class for only 1 close() exceptionHandler.onException(is::close); // I know it's redundant, exceptionHandler.onException(os::close); // but symmetry diff --git a/src/main/java/org/wickedsource/docxstamper/util/DocxImageExtractor.java b/src/main/java/org/wickedsource/docxstamper/util/DocxImageExtractor.java index 8d394511..7a97a69d 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/DocxImageExtractor.java +++ b/src/main/java/org/wickedsource/docxstamper/util/DocxImageExtractor.java @@ -58,7 +58,7 @@ private static Pic getPic(R run) { } private String getImageRelPartName(String imageRelId) { - // TODO : find a better way to find image rel part name in source part store + // TODO_LATER: find a better way to find image rel part name in source part store return wordprocessingMLPackage .getMainDocumentPart() .getRelationshipsPart() diff --git a/src/main/java/org/wickedsource/docxstamper/util/ObjectDeleter.java b/src/main/java/org/wickedsource/docxstamper/util/ObjectDeleter.java index 62ac18f6..7495a059 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/ObjectDeleter.java +++ b/src/main/java/org/wickedsource/docxstamper/util/ObjectDeleter.java @@ -66,7 +66,7 @@ private static void deleteFromCell(Tc cell, P paragraph) { if (TableCellUtil.hasNoParagraphOrTable(cell)) { TableCellUtil.addEmptyParagraph(cell); } - // TODO: find out why border lines are removed in some cells after having deleted a paragraph + // TODO_LATER: find out why border lines are removed in some cells after having deleted a paragraph } private static void deleteFromCell(Tc cell, Object obj) { @@ -77,7 +77,7 @@ private static void deleteFromCell(Tc cell, Object obj) { if (TableCellUtil.hasNoParagraphOrTable(cell)) { TableCellUtil.addEmptyParagraph(cell); } - // TODO: find out why border lines are removed in some cells after having deleted a paragraph + // TODO_LATER: find out why border lines are removed in some cells after having deleted a paragraph } /** diff --git a/src/main/java/pro/verron/docxstamper/api/Comment.java b/src/main/java/pro/verron/docxstamper/api/Comment.java index 75754d47..b024ddda 100644 --- a/src/main/java/pro/verron/docxstamper/api/Comment.java +++ b/src/main/java/pro/verron/docxstamper/api/Comment.java @@ -22,9 +22,19 @@ public interface Comment { * Retrieves the elements in the document that are between the comment range anchors. * * @return a list of objects representing the elements between the comment range anchors. - * TODO: Rename to be a more generic + * @deprecated removed for replacement by {@link Comment#getElements()} which doesn't imply a specific usage */ - List getRepeatElements(); + @Deprecated(since = "1.6.8", forRemoval = true) + default List getRepeatElements() { + return getElements(); + } + + /** + * Retrieves the elements in the document that are between the comment range anchors. + * + * @return a list of objects representing the elements between the comment range anchors. + */ + List getElements(); /** * Creates a new document containing only the elements between the comment range anchors. @@ -34,8 +44,8 @@ public interface Comment { * @return a new document containing only the elements between the comment range anchors. * * @throws Exception if the sub template could not be created. - * TODO: Remove from this interface and move to an utility class */ + // TODO: Remove from this interface and move to an utility class WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) throws Exception; @@ -43,9 +53,10 @@ WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) * Tries to build a subtemplate from the given WordprocessingMLPackage document. * * @param document the source document from which to build the subtemplate + * * @return the built subtemplate as a WordprocessingMLPackage - * TODO: Remove from this interface and move to an utility class */ + // TODO: Remove from this interface and move to an utility class WordprocessingMLPackage tryBuildingSubtemplate(WordprocessingMLPackage document); /** @@ -59,8 +70,8 @@ WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) * Sets the {@link CommentRangeEnd} object associated with this comment. * * @param commentRangeEnd the {@link CommentRangeEnd} object to set - * TODO: Remove the setting method from interface to increase immutability */ + // TODO: Remove the setting method from interface to increase immutability void setCommentRangeEnd(CommentRangeEnd commentRangeEnd); /** @@ -74,8 +85,8 @@ WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) * Sets the CommentRangeStart object associated with this comment. * * @param commentRangeStart the CommentRangeStart object to set - * TODO: Remove the setting method from interface to increase immutability */ + // TODO: Remove the setting method from interface to increase immutability void setCommentRangeStart(CommentRangeStart commentRangeStart); /** @@ -89,8 +100,8 @@ WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) * Sets the comment reference for this comment. * * @param commentReference the comment reference to set - * TODO: Remove the setting method from interface to increase immutability */ + // TODO: Remove the setting method from interface to increase immutability void setCommentReference(R.CommentReference commentReference); /** @@ -104,8 +115,8 @@ WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) * Sets the children of the comment. * * @param comments the set of Comment objects representing the children of the comment - * TODO: Remove the setting method from interface to increase immutability */ + // TODO: Remove the setting method from interface to increase immutability void setChildren(Set comments); /** @@ -119,7 +130,7 @@ WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) * Sets the comment for this object. * * @param comment the comment to set - * TODO: Remove the setting method from interface to increase immutability */ + // TODO: Remove the setting method from interface to increase immutability void setComment(Comments.Comment comment); } diff --git a/src/main/java/pro/verron/docxstamper/api/Image.java b/src/main/java/pro/verron/docxstamper/api/Image.java index 838af604..733dce08 100644 --- a/src/main/java/pro/verron/docxstamper/api/Image.java +++ b/src/main/java/pro/verron/docxstamper/api/Image.java @@ -18,12 +18,12 @@ public sealed class Image permits org.wickedsource.docxstamper.replace.typeresolver.image.Image { /** - * todo: make private asap + * TODO_LATER: make private asap */ protected final byte[] imageBytes; /** - * todo: make private asap + * TODO_LATER: make private asap */ protected Integer maxWidth; diff --git a/src/main/java/pro/verron/docxstamper/api/OfficeStamperConfiguration.java b/src/main/java/pro/verron/docxstamper/api/OfficeStamperConfiguration.java index 5f0ca4fe..e2bda3cc 100644 --- a/src/main/java/pro/verron/docxstamper/api/OfficeStamperConfiguration.java +++ b/src/main/java/pro/verron/docxstamper/api/OfficeStamperConfiguration.java @@ -180,7 +180,7 @@ OfficeStamperConfiguration addCommentProcessor( String getUnresolvedExpressionsDefaultValue(); /** - * TODO:javadoc + * TODO: javadoc */ String getLineBreakPlaceholder(); diff --git a/src/main/java/pro/verron/docxstamper/core/StandardComment.java b/src/main/java/pro/verron/docxstamper/core/StandardComment.java index 1cffed8c..b260174c 100644 --- a/src/main/java/pro/verron/docxstamper/core/StandardComment.java +++ b/src/main/java/pro/verron/docxstamper/core/StandardComment.java @@ -24,13 +24,13 @@ * @since 1.0.2 */ public class StandardComment - implements Comment { + implements Comment { - private final Set children = new HashSet<>(); - private Comments.Comment comment; - private CommentRangeStart commentRangeStart; - private CommentRangeEnd commentRangeEnd; - private CommentReference commentReference; + private final Set children = new HashSet<>(); + private Comments.Comment comment; + private CommentRangeStart commentRangeStart; + private CommentRangeEnd commentRangeEnd; + private CommentReference commentReference; private void extractedSubComments( List commentList, @@ -67,18 +67,18 @@ public Set getChildren() { return children; } - /** - *

getParent.

- * - * @return the comment's author. - */ - @Override - public ContentAccessor getParent() { - return findGreatestCommonParent( - getCommentRangeEnd().getParent(), - (ContentAccessor) getCommentRangeStart().getParent() - ); - } + /** + *

getParent.

+ * + * @return the comment's author. + */ + @Override + public ContentAccessor getParent() { + return findGreatestCommonParent( + getCommentRangeEnd().getParent(), + (ContentAccessor) getCommentRangeStart().getParent() + ); + } private ContentAccessor findGreatestCommonParent(Object end, ContentAccessor start) { if (depthElementSearch(end, start)) { @@ -110,29 +110,28 @@ private ContentAccessor findInsertableParent(ContentAccessor searchFrom) { return searchFrom; } - /** - *

getRepeatElements.

- * - * @return the elements in the document that are between the comment range anchors. - */ - @Override - public List getRepeatElements() { - List repeatElements = new ArrayList<>(); - boolean startFound = false; - for (Object element : getParent().getContent()) { - if (!startFound - && depthElementSearch(getCommentRangeStart(), element)) { - startFound = true; - } - if (startFound) { - repeatElements.add(element); - if (depthElementSearch(getCommentRangeEnd(), element)) { - break; - } - } - } - return repeatElements; - } + /** + *

getRepeatElements.

+ * + * @return the elements in the document that are between the comment range anchors. + */ + @Override + public List getElements() { + List repeatElements = new ArrayList<>(); + boolean startFound = false; + for (Object element : getParent().getContent()) { + if (!startFound && depthElementSearch(getCommentRangeStart(), element)) { + startFound = true; + } + if (startFound) { + repeatElements.add(element); + if (depthElementSearch(getCommentRangeEnd(), element)) { + break; + } + } + } + return repeatElements; + } private void removeCommentAnchorsFromFinalElements(List finalRepeatElements) { ContentAccessor fakeBody = () -> finalRepeatElements; @@ -143,101 +142,114 @@ public void setChildren(Set children) { this.children.addAll(children); } - /** - * Creates a new document containing only the elements between the comment range anchors. - * - * @param document the document from which to copy the elements. - * @return a new document containing only the elements between the comment range anchors. - * @throws Exception if the sub template could not be created. - */ - @Override - public WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) throws Exception { - List repeatElements = getRepeatElements(); - - WordprocessingMLPackage subDocument = WordprocessingMLPackage.createPackage(); - MainDocumentPart subDocumentMainDocumentPart = subDocument.getMainDocumentPart(); - - CommentsPart commentsPart = new CommentsPart(); - subDocumentMainDocumentPart.addTargetPart(commentsPart); - - // copy the elements to repeat without comment range anchors - List finalRepeatElements = repeatElements.stream().map(XmlUtils::deepCopy).collect(Collectors.toList()); - removeCommentAnchorsFromFinalElements(finalRepeatElements); - subDocumentMainDocumentPart.getContent().addAll(finalRepeatElements); - - // copy the images from parent document using the original repeat elements - ObjectFactory wmlObjectFactory = Context.getWmlObjectFactory(); - ContentAccessor fakeBody = wmlObjectFactory.createBody(); - fakeBody.getContent().addAll(repeatElements); - DocumentUtil.walkObjectsAndImportImages(fakeBody, document, subDocument); - - Comments comments = wmlObjectFactory.createComments(); - extractedSubComments(comments.getComment(), this.getChildren()); - commentsPart.setContents(comments); - - return subDocument; - } - - /** - * Creates a new document containing only the elements between the comment range anchors. - * If the sub template could not be created, a - * {@link DocxStamperException} is thrown. - * - * @param document the document from which to copy the elements. - * @return a new document containing only the elements between the comment range anchors. - */ - @Override - public WordprocessingMLPackage tryBuildingSubtemplate( - WordprocessingMLPackage document - ) { - try { - return getSubTemplate(document); - } catch (Exception e) { - throw new DocxStamperException(e); - } - } - - /** - *

Getter for the field commentRangeEnd.

- * - * @return a {@link CommentRangeEnd} object - */ - @Override - public CommentRangeEnd getCommentRangeEnd() { - return commentRangeEnd; - } + /** + * Creates a new document containing only the elements between the comment range anchors. + * + * @param document the document from which to copy the elements. + * + * @return a new document containing only the elements between the comment range anchors. + * + * @throws Exception if the sub template could not be created. + */ + @Override + public WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) + throws Exception { + List repeatElements = getRepeatElements(); + + WordprocessingMLPackage subDocument = WordprocessingMLPackage.createPackage(); + MainDocumentPart subDocumentMainDocumentPart = subDocument.getMainDocumentPart(); + + CommentsPart commentsPart = new CommentsPart(); + subDocumentMainDocumentPart.addTargetPart(commentsPart); + + // copy the elements to repeat without comment range anchors + List finalRepeatElements = repeatElements.stream() + .map(XmlUtils::deepCopy) + .collect(Collectors.toList()); + removeCommentAnchorsFromFinalElements(finalRepeatElements); + subDocumentMainDocumentPart.getContent() + .addAll(finalRepeatElements); + + // copy the images from parent document using the original repeat elements + ObjectFactory wmlObjectFactory = Context.getWmlObjectFactory(); + ContentAccessor fakeBody = wmlObjectFactory.createBody(); + fakeBody.getContent() + .addAll(repeatElements); + DocumentUtil.walkObjectsAndImportImages(fakeBody, document, subDocument); + + Comments comments = wmlObjectFactory.createComments(); + extractedSubComments(comments.getComment(), this.getChildren()); + commentsPart.setContents(comments); + + return subDocument; + } + + /** + * Creates a new document containing only the elements between the comment range anchors. + * If the sub template could not be created, a + * {@link DocxStamperException} is thrown. + * + * @param document the document from which to copy the elements. + * + * @return a new document containing only the elements between the comment range anchors. + */ + @Override + public WordprocessingMLPackage tryBuildingSubtemplate( + WordprocessingMLPackage document + ) { + try { + return getSubTemplate(document); + } catch (Exception e) { + throw new DocxStamperException(e); + } + } + + + /** + *

Getter for the field commentRangeEnd.

+ * + * @return a {@link CommentRangeEnd} object + */ + @Override + public CommentRangeEnd getCommentRangeEnd() { + return commentRangeEnd; + } + + + /** + *

Getter for the field commentRangeStart.

+ * + * @return a {@link CommentRangeStart} object + */ + @Override + public CommentRangeStart getCommentRangeStart() { + return commentRangeStart; + } + + + /** + *

Getter for the field commentReference.

+ * + * @return a {@link CommentReference} object + */ + @Override + public CommentReference getCommentReference() { + return commentReference; + } + + + /** + *

Getter for the field comment.

+ * + * @return a {@link Comments.Comment} object + */ + @Override + public Comments.Comment getComment() { + return comment; + } + + public void setComment(Comments.Comment comment) { + this.comment = comment; + } - /** - *

Getter for the field commentRangeStart.

- * - * @return a {@link CommentRangeStart} object - */ - @Override - public CommentRangeStart getCommentRangeStart() { - return commentRangeStart; - } - - /** - *

Getter for the field commentReference.

- * - * @return a {@link CommentReference} object - */ - @Override - public CommentReference getCommentReference() { - return commentReference; - } - - /** - *

Getter for the field comment.

- * - * @return a {@link Comments.Comment} object - */ - @Override - public Comments.Comment getComment() { - return comment; - } - - public void setComment(Comments.Comment comment) { - this.comment = comment; - } } From d45bc1ac44e370f81ad59cc79aa7f3dd3083a3d5 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 2 May 2024 07:14:18 +0800 Subject: [PATCH 086/134] Refactor StandardComment class for readability Reordered the methods in the StandardComment class for better readability and maintaining conventions. The getters and setters are now grouped together, and utility methods are located towards the bottom of the class definition. --- .../docxstamper/core/StandardComment.java | 145 +++++++++--------- 1 file changed, 71 insertions(+), 74 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/core/StandardComment.java b/src/main/java/pro/verron/docxstamper/core/StandardComment.java index b260174c..82258c61 100644 --- a/src/main/java/pro/verron/docxstamper/core/StandardComment.java +++ b/src/main/java/pro/verron/docxstamper/core/StandardComment.java @@ -32,41 +32,6 @@ public class StandardComment private CommentRangeEnd commentRangeEnd; private CommentReference commentReference; - private void extractedSubComments( - List commentList, - Set commentChildren - ) { - Queue q = new ArrayDeque<>(commentChildren); - while (!q.isEmpty()) { - Comment element = q.remove(); - commentList.add(element.getComment()); - if (element.getChildren() != null) - q.addAll(element.getChildren()); - } - } - - public void setCommentRangeStart(CommentRangeStart commentRangeStart) { - this.commentRangeStart = commentRangeStart; - } - - public void setCommentRangeEnd(CommentRangeEnd commentRangeEnd) { - this.commentRangeEnd = commentRangeEnd; - } - - public void setCommentReference(CommentReference commentReference) { - this.commentReference = commentReference; - } - - /** - *

Getter for the field children.

- * - * @return a {@link Set} object - */ - @Override - public Set getChildren() { - return children; - } - /** *

getParent.

* @@ -80,36 +45,6 @@ public ContentAccessor getParent() { ); } - private ContentAccessor findGreatestCommonParent(Object end, ContentAccessor start) { - if (depthElementSearch(end, start)) { - return findInsertableParent(start); - } - return findGreatestCommonParent(end, (ContentAccessor) ((Child) start).getParent()); - } - - private boolean depthElementSearch(Object searchTarget, Object content) { - content = XmlUtils.unwrap(content); - if (searchTarget.equals(content)) { - return true; - } else if (content instanceof ContentAccessor contentAccessor) { - for (Object object : contentAccessor.getContent()) { - Object unwrappedObject = XmlUtils.unwrap(object); - if (searchTarget.equals(unwrappedObject) - || depthElementSearch(searchTarget, unwrappedObject)) { - return true; - } - } - } - return false; - } - - private ContentAccessor findInsertableParent(ContentAccessor searchFrom) { - if (!(searchFrom instanceof Tc || searchFrom instanceof Body)) { - return findInsertableParent((ContentAccessor) ((Child) searchFrom).getParent()); - } - return searchFrom; - } - /** *

getRepeatElements.

* @@ -133,15 +68,6 @@ public List getElements() { return repeatElements; } - private void removeCommentAnchorsFromFinalElements(List finalRepeatElements) { - ContentAccessor fakeBody = () -> finalRepeatElements; - CommentUtil.deleteCommentFromElement(fakeBody.getContent(), getComment().getId()); - } - - public void setChildren(Set children) { - this.children.addAll(children); - } - /** * Creates a new document containing only the elements between the comment range anchors. * @@ -204,6 +130,23 @@ public WordprocessingMLPackage tryBuildingSubtemplate( } } + private void removeCommentAnchorsFromFinalElements(List finalRepeatElements) { + ContentAccessor fakeBody = () -> finalRepeatElements; + CommentUtil.deleteCommentFromElement(fakeBody.getContent(), getComment().getId()); + } + + private void extractedSubComments( + List commentList, + Set commentChildren + ) { + Queue q = new ArrayDeque<>(commentChildren); + while (!q.isEmpty()) { + Comment element = q.remove(); + commentList.add(element.getComment()); + if (element.getChildren() != null) + q.addAll(element.getChildren()); + } + } /** *

Getter for the field commentRangeEnd.

@@ -215,6 +158,9 @@ public CommentRangeEnd getCommentRangeEnd() { return commentRangeEnd; } + public void setCommentRangeEnd(CommentRangeEnd commentRangeEnd) { + this.commentRangeEnd = commentRangeEnd; + } /** *

Getter for the field commentRangeStart.

@@ -226,6 +172,9 @@ public CommentRangeStart getCommentRangeStart() { return commentRangeStart; } + public void setCommentRangeStart(CommentRangeStart commentRangeStart) { + this.commentRangeStart = commentRangeStart; + } /** *

Getter for the field commentReference.

@@ -237,6 +186,23 @@ public CommentReference getCommentReference() { return commentReference; } + public void setCommentReference(CommentReference commentReference) { + this.commentReference = commentReference; + } + + /** + *

Getter for the field children.

+ * + * @return a {@link Set} object + */ + @Override + public Set getChildren() { + return children; + } + + public void setChildren(Set children) { + this.children.addAll(children); + } /** *

Getter for the field comment.

@@ -252,4 +218,35 @@ public void setComment(Comments.Comment comment) { this.comment = comment; } + private ContentAccessor findGreatestCommonParent(Object end, ContentAccessor start) { + if (depthElementSearch(end, start)) { + return findInsertableParent(start); + } + return findGreatestCommonParent(end, (ContentAccessor) ((Child) start).getParent()); + } + + private boolean depthElementSearch(Object searchTarget, Object content) { + content = XmlUtils.unwrap(content); + if (searchTarget.equals(content)) { + return true; + } + else if (content instanceof ContentAccessor contentAccessor) { + for (Object object : contentAccessor.getContent()) { + Object unwrappedObject = XmlUtils.unwrap(object); + if (searchTarget.equals(unwrappedObject) + || depthElementSearch(searchTarget, unwrappedObject)) { + return true; + } + } + } + return false; + } + + private ContentAccessor findInsertableParent(ContentAccessor searchFrom) { + if (!(searchFrom instanceof Tc || searchFrom instanceof Body)) { + return findInsertableParent((ContentAccessor) ((Child) searchFrom).getParent()); + } + return searchFrom; + } + } From f56803107743f676740436b756dbb93deeed02c3 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 4 May 2024 20:34:49 +0800 Subject: [PATCH 087/134] Add WordprocessingMLPackage to StandardComment Added WordprocessingMLPackage as a parameter to StandardComment's constructor and also as a return type for the getDocument method. This helps to retain details about the document within the StandardComment class. Changed instance creation of StandardComment in CommentUtil to align with the modified constructor. --- .../repeat/RepeatDocPartProcessor.java | 4 +- .../pro/verron/docxstamper/api/Comment.java | 2 + .../verron/docxstamper/core/CommentUtil.java | 2 +- .../docxstamper/core/StandardComment.java | 67 +++++++++++++++++++ 4 files changed, 72 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java index 86c3f32b..27891b3d 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java @@ -111,9 +111,9 @@ public void repeatDocPart(List contexts) { contexts = Collections.emptyList(); Comment currentComment = getCurrentCommentWrapper(); - List repeatElements = currentComment.getRepeatElements(); + List elements = currentComment.getElements(); - if (!repeatElements.isEmpty()) { + if (!elements.isEmpty()) { this.contexts.put(currentComment, contexts); } } diff --git a/src/main/java/pro/verron/docxstamper/api/Comment.java b/src/main/java/pro/verron/docxstamper/api/Comment.java index b024ddda..d66f8abc 100644 --- a/src/main/java/pro/verron/docxstamper/api/Comment.java +++ b/src/main/java/pro/verron/docxstamper/api/Comment.java @@ -133,4 +133,6 @@ WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) */ // TODO: Remove the setting method from interface to increase immutability void setComment(Comments.Comment comment); + + WordprocessingMLPackage getDocument(); } diff --git a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java index f4dc98dd..5c053043 100644 --- a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java +++ b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java @@ -334,7 +334,7 @@ protected void onCommentRangeStart(CommentRangeStart commentRangeStart) { Comment comment = allComments.get( commentRangeStart.getId()); if (comment == null) { - comment = new StandardComment(); + comment = new StandardComment(document); allComments.put(commentRangeStart.getId(), comment); if (stack.isEmpty()) { rootComments.put(commentRangeStart.getId(), diff --git a/src/main/java/pro/verron/docxstamper/core/StandardComment.java b/src/main/java/pro/verron/docxstamper/core/StandardComment.java index 82258c61..ad5e5583 100644 --- a/src/main/java/pro/verron/docxstamper/core/StandardComment.java +++ b/src/main/java/pro/verron/docxstamper/core/StandardComment.java @@ -27,11 +27,74 @@ public class StandardComment implements Comment { private final Set children = new HashSet<>(); + private final WordprocessingMLPackage document; private Comments.Comment comment; private CommentRangeStart commentRangeStart; private CommentRangeEnd commentRangeEnd; private CommentReference commentReference; + public StandardComment(WordprocessingMLPackage document) { + this.document = document; + } + + /** + * Creates a new document containing only the elements between the comment range anchors. + * + * @param document the document from which to copy the elements. + * + * @return a new document containing only the elements between the comment range anchors. + * + * @throws Exception if the sub template could not be created. + */ + @Override + public WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) + throws Exception { + List repeatElements = getElements(); + return createSubWordDocument(this, repeatElements); + } + + private static WordprocessingMLPackage createSubWordDocument( + Comment comment, + List repeatElements + ) + throws InvalidFormatException { + WordprocessingMLPackage target = WordprocessingMLPackage.createPackage(); + MainDocumentPart targetMainPart = target.getMainDocumentPart(); + + CommentsPart commentsPart = new CommentsPart(); + targetMainPart.addTargetPart(commentsPart); + + // copy the elements to repeat without comment range anchors + List finalRepeatElements = repeatElements.stream() + .map(XmlUtils::deepCopy) + .collect(Collectors.toList()); + StandardComment.removeCommentAnchorsFromFinalElements(comment, finalRepeatElements); + targetMainPart.getContent() + .addAll(finalRepeatElements); + + // copy the images from parent document using the original repeat elements + ObjectFactory wmlObjectFactory = Context.getWmlObjectFactory(); + ContentAccessor fakeBody = wmlObjectFactory.createBody(); + fakeBody.getContent() + .addAll(repeatElements); + DocumentUtil.walkObjectsAndImportImages(fakeBody, comment.getDocument(), target); + + Comments comments = wmlObjectFactory.createComments(); + StandardComment.extractedSubComments(comments.getComment(), comment.getChildren()); + commentsPart.setContents(comments); + return target; + } + + private static void removeCommentAnchorsFromFinalElements( + Comment comment, + List finalRepeatElements + ) { + ContentAccessor fakeBody = () -> finalRepeatElements; + CommentUtil.deleteCommentFromElement(fakeBody.getContent(), + comment.getComment() + .getId()); + } + /** *

getParent.

* @@ -218,6 +281,10 @@ public void setComment(Comments.Comment comment) { this.comment = comment; } + @Override public WordprocessingMLPackage getDocument() { + return document; + } + private ContentAccessor findGreatestCommonParent(Object end, ContentAccessor start) { if (depthElementSearch(end, start)) { return findInsertableParent(start); From 4fe6c7bcf288dbe6e77d73b7747c73e2d99b6e79 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 4 May 2024 20:40:10 +0800 Subject: [PATCH 088/134] Refactor 'repeatDocPart' method in RepeatDocPartProcessor.java The 'repeatDocPart' method has been relocated within the RepeatDocPartProcessor.java. Additionally, modifications have been made to the 'commitChanges' method to optimize the processing of comment entry values using 'requireNonNull' and the 'var' keyword for simpler and more readable coding. --- .../repeat/RepeatDocPartProcessor.java | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java index 27891b3d..57535d4f 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java @@ -28,6 +28,7 @@ import java.util.function.Supplier; import static java.util.Collections.singletonList; +import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toMap; import static org.wickedsource.docxstamper.util.DocumentUtil.walkObjectsAndImportImages; @@ -102,22 +103,6 @@ public static CommentProcessor newInstance( return new RepeatDocPartProcessor(pr, stamper, Collections::emptyList); } - /** - * {@inheritDoc} - */ - @Override - public void repeatDocPart(List contexts) { - if (contexts == null) - contexts = Collections.emptyList(); - - Comment currentComment = getCurrentCommentWrapper(); - List elements = currentComment.getElements(); - - if (!elements.isEmpty()) { - this.contexts.put(currentComment, contexts); - } - } - private static List documentAsInsertableElements( WordprocessingMLPackage subDocument, boolean oddNumberOfBreaks, @@ -143,6 +128,22 @@ private static List documentAsInsertableElements( return inserts; } + /** + * {@inheritDoc} + */ + @Override + public void repeatDocPart(List contexts) { + if (contexts == null) + contexts = Collections.emptyList(); + + Comment currentComment = getCurrentCommentWrapper(); + List elements = currentComment.getElements(); + + if (!elements.isEmpty()) { + this.contexts.put(currentComment, contexts); + } + } + private List stampSubDocuments( WordprocessingMLPackage document, List expressionContexts, @@ -193,13 +194,11 @@ private List stampSubDocuments( @Override public void commitChanges(WordprocessingMLPackage document) { for (Entry> entry : this.contexts.entrySet()) { - Comment comment = entry.getKey(); - List expressionContexts = entry.getValue(); - ContentAccessor gcp = Objects.requireNonNull( - comment.getParent()); - List repeatElements = comment.getRepeatElements(); - WordprocessingMLPackage subTemplate = comment.tryBuildingSubtemplate( - document); + var comment = entry.getKey(); + var expressionContexts = entry.getValue(); + var gcp = requireNonNull(comment.getParent()); + var repeatElements = comment.getElements(); + var subTemplate = comment.tryBuildingSubtemplate(document); SectPr previousSectionBreak = SectionUtil.getPreviousSectionBreakIfPresent( repeatElements.get(0), gcp); boolean oddNumberOfBreaks = SectionUtil.isOddNumberOfSectionBreaks( From 7c7265666b8db280bc288abde2b9b9ea8a29f426 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 4 May 2024 20:48:24 +0800 Subject: [PATCH 089/134] Update Exception class and mark unused methods as deprecated This commit replaces the usage of DocxStamperException with OfficeStamperException and marks several get methods in DocumentUtil as deprecated as they are not used internally. Further code optimization and cleanup was performed to enhance readability. --- .../docxstamper/util/DocumentUtil.java | 91 +++++++++++-------- 1 file changed, 55 insertions(+), 36 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/util/DocumentUtil.java b/src/main/java/org/wickedsource/docxstamper/util/DocumentUtil.java index d97c2db5..e275c092 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/DocumentUtil.java +++ b/src/main/java/org/wickedsource/docxstamper/util/DocumentUtil.java @@ -8,7 +8,7 @@ import org.docx4j.openpackaging.parts.relationships.Namespaces; import org.docx4j.openpackaging.parts.relationships.RelationshipsPart; import org.docx4j.wml.*; -import org.wickedsource.docxstamper.api.DocxStamperException; +import pro.verron.docxstamper.api.OfficeStamperException; import java.util.*; import java.util.stream.Stream; @@ -22,17 +22,21 @@ * @since 1.4.7 */ public class DocumentUtil { + private DocumentUtil() { - throw new DocxStamperException( - "Utility classes shouldn't be instantiated"); + throw new OfficeStamperException("Utility classes shouldn't be instantiated"); } /** * Retrieve all the paragraphs from a document * * @param parentObject the document to get the paragraphs from + * * @return a list of paragraphs + * + * @deprecated method not used internally so will be removed */ + @Deprecated(since = "1.6.8", forRemoval = true) public static List

getParagraphsFromObject(Object parentObject) { return streamElements(parentObject, P.class).toList(); } @@ -43,6 +47,7 @@ public static List

getParagraphsFromObject(Object parentObject) { * @param object the object to get the elements from * @param elementClass the class of the elements to get * @param the type of the elements to get + * * @return a stream of the elements */ public static Stream streamElements( @@ -61,6 +66,7 @@ public static Stream streamElements( * @param document the document to get the elements from * @param elementClass the class of the elements to get * @param the type of the elements to get + * * @return a stream of the elements */ private static Stream streamDocumentElements( @@ -68,14 +74,14 @@ private static Stream streamDocumentElements( Class elementClass ) { RelationshipsPart mainParts = document.getMainDocumentPart() - .getRelationshipsPart(); + .getRelationshipsPart(); return Stream.of( - streamElements(mainParts, Namespaces.HEADER, elementClass), - streamObjectElements(document.getMainDocumentPart(), - elementClass), - streamElements(mainParts, Namespaces.FOOTER, elementClass) - ) - .reduce(Stream.empty(), Stream::concat); + streamElements(mainParts, Namespaces.HEADER, elementClass), + streamObjectElements(document.getMainDocumentPart(), + elementClass), + streamElements(mainParts, Namespaces.FOOTER, elementClass) + ) + .reduce(Stream.empty(), Stream::concat); } private static Stream streamObjectElements( @@ -85,7 +91,7 @@ private static Stream streamObjectElements( ClassFinder finder = new ClassFinder(elementClass); TraversalUtil.visit(obj, finder); return finder.results.stream() - .map(elementClass::cast); + .map(elementClass::cast); } private static Stream streamElements( @@ -104,8 +110,12 @@ private static Stream streamElements( * Retrieve all the tables from an object. * * @param parentObject the object to get the tables from + * * @return a list of tables + * + * @deprecated method not used internally so will be removed */ + @Deprecated(since = "1.6.8", forRemoval = true) public static List getTableFromObject(Object parentObject) { return streamElements(parentObject, Tbl.class).toList(); } @@ -114,8 +124,12 @@ public static List getTableFromObject(Object parentObject) { * Retrieve all the rows from an object. * * @param parentObject the object to get the rows from + * * @return a list of rows + * + * @deprecated method not used internally so will be removed */ + @Deprecated(since = "1.6.8", forRemoval = true) public static List

getTableRowsFromObject(Object parentObject) { return streamElements(parentObject, Tr.class).toList(); } @@ -124,8 +138,12 @@ public static List getTableRowsFromObject(Object parentObject) { * Retrieve all the cells from an object. * * @param parentObject the object to get the cells from + * * @return a list of cells + * + * @deprecated method not used internally so will be removed */ + @Deprecated(since = "1.6.8", forRemoval = true) public static List getTableCellsFromObject(Object parentObject) { return streamElements(parentObject, Tc.class).toList(); } @@ -134,22 +152,25 @@ public static List getTableCellsFromObject(Object parentObject) { * Retrieve the first element from an object. * * @param subDocument the object to get the first element from + * * @return the first element */ public static Object lastElement(WordprocessingMLPackage subDocument) { - return new ArrayDeque<>(subDocument.getMainDocumentPart() - .getContent()).getLast(); + var mainDocumentPart = subDocument.getMainDocumentPart(); + var mainDocumentPartContent = mainDocumentPart.getContent(); + return mainDocumentPartContent.get(mainDocumentPartContent.size() - 1); } /** * Retrieve the last element from an object. * * @param subDocument the object to get the last element from + * * @return the last element */ public static List allElements(WordprocessingMLPackage subDocument) { return subDocument.getMainDocumentPart() - .getContent(); + .getContent(); } /** @@ -157,6 +178,7 @@ public static List allElements(WordprocessingMLPackage subDocument) { * * @param source source document containing image files. * @param target target document to add image files to. + * * @return a {@link java.util.Map} object */ public static Map walkObjectsAndImportImages( @@ -164,8 +186,8 @@ public static Map walkObjectsAndImportImages( WordprocessingMLPackage target ) { return walkObjectsAndImportImages(source.getMainDocumentPart(), - source, - target); + source, + target); } /** @@ -174,6 +196,7 @@ public static Map walkObjectsAndImportImages( * @param container source container to walk. * @param source source document containing image files. * @param target target document to add image files to. + * * @return a {@link java.util.Map} object */ public static Map walkObjectsAndImportImages( @@ -190,19 +213,14 @@ public static Map walkObjectsAndImportImages( Object currentObj = queue.remove(); if (currentObj instanceof R currentR && isImageRun(currentR)) { - DocxImageExtractor docxImageExtractor = new DocxImageExtractor( - source); - byte[] imageData = docxImageExtractor.getRunDrawingData( - currentR); - Integer maxWidth = docxImageExtractor.getRunDrawingMaxWidth( - currentR); - BinaryPartAbstractImage imagePart = tryCreateImagePart( - target, - imageData); - R runWithImage = RunUtil.createRunWithImage(maxWidth, - imagePart); + var docxImageExtractor = new DocxImageExtractor(source); + var imageData = docxImageExtractor.getRunDrawingData(currentR); + var maxWidth = docxImageExtractor.getRunDrawingMaxWidth(currentR); + var imagePart = tryCreateImagePart(target, imageData); + var runWithImage = RunUtil.createRunWithImage(maxWidth, imagePart); replacements.put(currentR, runWithImage); - } else if (currentObj instanceof ContentAccessor contentAccessor) + } + else if (currentObj instanceof ContentAccessor contentAccessor) queue.addAll(contentAccessor.getContent()); } } @@ -213,15 +231,16 @@ public static Map walkObjectsAndImportImages( * Check if a run contains an embedded image. * * @param run the run to analyze + * * @return true if the run contains an image, false otherwise. */ private static boolean isImageRun(R run) { return run.getContent() - .stream() - .filter(JAXBElement.class::isInstance) - .map(JAXBElement.class::cast) - .map(JAXBElement::getValue) - .anyMatch(Drawing.class::isInstance); + .stream() + .filter(JAXBElement.class::isInstance) + .map(JAXBElement.class::cast) + .map(JAXBElement::getValue) + .anyMatch(Drawing.class::isInstance); } private static BinaryPartAbstractImage tryCreateImagePart( @@ -229,10 +248,9 @@ private static BinaryPartAbstractImage tryCreateImagePart( byte[] imageData ) { try { - return BinaryPartAbstractImage.createImagePart(destDocument, - imageData); + return BinaryPartAbstractImage.createImagePart(destDocument, imageData); } catch (Exception e) { - throw new DocxStamperException(e); + throw new OfficeStamperException(e); } } @@ -240,6 +258,7 @@ private static BinaryPartAbstractImage tryCreateImagePart( * Retrieve all the runs from a document. * * @param document the document to get the runs from + * * @return the runs */ public static Stream

streamParagraphs(WordprocessingMLPackage document) { From cfa2717b5d81948c50494671a3f0c4ee1360e3a5 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 4 May 2024 20:53:25 +0800 Subject: [PATCH 090/134] Replace DocxStamperException with OfficeStamperException The DocxStamperException usage has been replaced with OfficeStamperException throughout the CommentUtil.java file. This change has been done to consistently use the project-specific exception. It affects several methods that previously used the deprecated exception for various error situations. --- .../verron/docxstamper/core/CommentUtil.java | 146 +++++++++--------- 1 file changed, 77 insertions(+), 69 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java index 5c053043..c7aa1bfe 100644 --- a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java +++ b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java @@ -10,10 +10,10 @@ import org.jvnet.jaxb2_commons.ppp.Child; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.util.walk.BaseDocumentWalker; import org.wickedsource.docxstamper.util.walk.DocumentWalker; import pro.verron.docxstamper.api.Comment; +import pro.verron.docxstamper.api.OfficeStamperException; import pro.verron.docxstamper.api.Placeholder; import java.math.BigInteger; @@ -40,7 +40,7 @@ public class CommentUtil { */ // TODO: Move to private for next version protected CommentUtil() { - throw new DocxStamperException("Utility class shouldn't be instantiated"); + throw new OfficeStamperException("Utility class shouldn't be instantiated"); } /** @@ -48,6 +48,7 @@ protected CommentUtil() { * * @param run the DOCX4J object whose comment to retrieve. * @param document the document that contains the object. + * * @return Optional of the comment, if found, Optional.empty() otherwise. */ public static Optional getCommentAround( @@ -59,15 +60,14 @@ public static Optional getCommentAround( try { return getComment(run, document, parent); } catch (Docx4JException e) { - throw new DocxStamperException( - "error accessing the comments of the document!", - e); + throw new OfficeStamperException("error accessing the comments of the document!", e); } } private static Optional getComment( R run, WordprocessingMLPackage document, ContentAccessor parent - ) throws Docx4JException { + ) + throws Docx4JException { CommentRangeStart possibleComment = null; boolean foundChild = false; for (Object contentElement : parent.getContent()) { @@ -103,21 +103,24 @@ else if (possibleComment != null && foundChild && unwrap( * * @param document the WordprocessingMLPackage document to search for the comment * @param id the ID of the comment to find + * * @return an Optional containing the Comment if found, or an empty Optional if not found + * * @throws Docx4JException if an error occurs while searching for the comment */ public static Optional findComment( WordprocessingMLPackage document, BigInteger id - ) throws Docx4JException { + ) + throws Docx4JException { var name = new PartName(WORD_COMMENTS_PART_NAME); var parts = document.getParts(); var wordComments = (CommentsPart) parts.get(name); var comments = wordComments.getContents(); return comments.getComment() - .stream() - .filter(comment -> comment.getId() - .equals(id)) - .findFirst(); + .stream() + .filter(comment -> comment.getId() + .equals(id)) + .findFirst(); } /** @@ -125,8 +128,10 @@ public static Optional findComment( * * @param object the DOCX4J object whose comment to retrieve. * @param document the document that contains the object. + * * @return an Expression representing the comment string. - * @throws DocxStamperException if an error occurs while retrieving the comment. + * + * @throws OfficeStamperException if an error occurs while retrieving the comment. * @deprecated This method's been deprecated since version 1.6.8 * and will be removed in the future. */ @@ -134,8 +139,8 @@ public static Optional findComment( public static Placeholder getCommentStringFor( ContentAccessor object, WordprocessingMLPackage document ) { - Comments.Comment comment = getCommentFor(object, - document).orElseThrow(); + var comment = getCommentFor(object, document) + .orElseThrow(OfficeStamperException::new); return getCommentString(comment); } @@ -147,6 +152,7 @@ public static Placeholder getCommentStringFor( * @param object the object whose comment to load. * @param document the document in which the object is embedded (needed to load the * comment from the comments.xml part). + * * @return the concatenated string of all text paragraphs within the * comment or null if the specified object is not commented. */ @@ -160,25 +166,21 @@ public static Optional getCommentFor( try { partName = new PartName(WORD_COMMENTS_PART_NAME); } catch (InvalidFormatException e) { - String message = String.format( - "Error while searching comment. Skipping object %s.", - object); - throw new DocxStamperException(message, e); + String message = String.format("Error while searching comment. Skipping object %s.", object); + throw new OfficeStamperException(message, e); } CommentsPart commentsPart = (CommentsPart) document.getParts() - .get(partName); + .get(partName); Comments comments; try { comments = commentsPart.getContents(); } catch (Docx4JException e) { - throw new DocxStamperException( - "error accessing the comments of the document!", - e); + throw new OfficeStamperException("error accessing the comments of the document!", e); } for (Comments.Comment comment : comments.getComment()) { - if (comment.getId() - .equals(id)) { + var commentId = comment.getId(); + if (commentId.equals(id)) { return Optional.of(comment); } } @@ -190,6 +192,7 @@ public static Optional getCommentFor( * Returns the string value of the specified comment object. * * @param comment a {@link Comments.Comment} object + * * @return a {@link String} object */ public static Placeholder getCommentString(Comments.Comment comment) { @@ -213,19 +216,19 @@ public static void deleteComment(Comment comment) { if (end != null) { ContentAccessor endParent = (ContentAccessor) end.getParent(); endParent.getContent() - .remove(end); + .remove(end); } CommentRangeStart start = comment.getCommentRangeStart(); if (start != null) { ContentAccessor startParent = (ContentAccessor) start.getParent(); startParent.getContent() - .remove(start); + .remove(start); } R.CommentReference reference = comment.getCommentReference(); if (reference != null) { ContentAccessor referenceParent = (ContentAccessor) reference.getParent(); referenceParent.getContent() - .remove(reference); + .remove(reference); } } @@ -233,6 +236,7 @@ public static void deleteComment(Comment comment) { * Extracts all comments from the given document. * * @param document the document to extract comments from. + * * @return a map of all comments, with the key being the comment id. */ public static Map getComments( @@ -259,20 +263,23 @@ public static void deleteCommentFromElement( Object unwrapped = unwrap(item); if (unwrapped instanceof CommentRangeStart crs) { if (crs.getId() - .equals(commentId)) { + .equals(commentId)) { elementsToRemove.add(item); } - } else if (unwrapped instanceof CommentRangeEnd cre) { + } + else if (unwrapped instanceof CommentRangeEnd cre) { if (cre.getId() - .equals(commentId)) { + .equals(commentId)) { elementsToRemove.add(item); } - } else if (unwrapped instanceof R.CommentReference rcr) { + } + else if (unwrapped instanceof R.CommentReference rcr) { if (rcr.getId() - .equals(commentId)) { + .equals(commentId)) { elementsToRemove.add(item); } - } else if (unwrapped instanceof ContentAccessor ca) { + } + else if (unwrapped instanceof ContentAccessor ca) { deleteCommentFromElement(ca.getContent(), commentId); } } @@ -287,7 +294,8 @@ private static Map cleanMalformedComments(Map cleanMalformedComments(Map cleanMalformedComments(Set children) { return children.stream() - .filter(comment -> { - if (isCommentMalformed(comment)) { - logger.error( - "Skipping malformed comment, missing range start and/or range end : {}", - getCommentContent(comment)); - return false; - } - comment.setChildren(cleanMalformedComments(comment.getChildren())); - return true; - }) - .collect(toSet()); + .filter(comment -> { + if (isCommentMalformed(comment)) { + logger.error( + "Skipping malformed comment, missing range start and/or range end : {}", + getCommentContent(comment)); + return false; + } + comment.setChildren(cleanMalformedComments(comment.getChildren())); + return true; + }) + .collect(toSet()); } private static String getCommentContent(Comment comment) { return comment.getComment() != null ? comment.getComment() - .getContent() - .stream() - .map(TextUtils::getText) - .collect(Collectors.joining("")) : ""; + .getContent() + .stream() + .map(TextUtils::getText) + .collect(Collectors.joining("")) : ""; } private static boolean isCommentMalformed(Comment comment) { - return comment.getCommentRangeStart() == null || comment.getCommentRangeEnd() == null || comment.getComment() == null; + return comment.getCommentRangeStart() == null || comment.getCommentRangeEnd() == null + || comment.getComment() == null; } private static void collectCommentRanges( @@ -331,18 +340,18 @@ private static void collectCommentRanges( DocumentWalker documentWalker = new BaseDocumentWalker(document.getMainDocumentPart()) { @Override protected void onCommentRangeStart(CommentRangeStart commentRangeStart) { - Comment comment = allComments.get( - commentRangeStart.getId()); + Comment comment = allComments.get(commentRangeStart.getId()); if (comment == null) { comment = new StandardComment(document); allComments.put(commentRangeStart.getId(), comment); if (stack.isEmpty()) { rootComments.put(commentRangeStart.getId(), - comment); - } else { + comment); + } + else { stack.peek() - .getChildren() - .add(comment); + .getChildren() + .add(comment); } } comment.setCommentRangeStart(commentRangeStart); @@ -352,24 +361,24 @@ protected void onCommentRangeStart(CommentRangeStart commentRangeStart) { @Override protected void onCommentRangeEnd(CommentRangeEnd commentRangeEnd) { Comment comment = allComments.get(commentRangeEnd.getId()); - if (comment == null) throw new DocxStamperException( - "Found a comment range end before the comment range start !"); + if (comment == null) + throw new OfficeStamperException("Found a comment range end before the comment range start !"); comment.setCommentRangeEnd(commentRangeEnd); if (stack.isEmpty()) return; - if (stack.peek() - .equals(comment)) stack.remove(); - else throw new DocxStamperException( - "Cannot figure which comment contains the other !"); + var peek = stack.peek(); + if (peek.equals(comment)) + stack.remove(); + else throw new OfficeStamperException("Cannot figure which comment contains the other !"); } @Override protected void onCommentReference(R.CommentReference commentReference) { Comment comment = allComments.get(commentReference.getId()); - if (comment == null) throw new DocxStamperException( - "Found a comment reference before the comment range start !"); + if (comment == null) + throw new OfficeStamperException("Found a comment reference before the comment range start !"); comment.setCommentReference(commentReference); } }; @@ -381,15 +390,14 @@ private static void collectComments( WordprocessingMLPackage document ) { try { - CommentsPart commentsPart = (CommentsPart) document.getParts() - .get(new PartName(WORD_COMMENTS_PART_NAME)); - if (commentsPart == null) {return;} + var documentParts = document.getParts(); + var commentsPart = (CommentsPart) documentParts.get(new PartName(WORD_COMMENTS_PART_NAME)); + if (commentsPart == null) return; var commentsPartContents = commentsPart.getContents(); for (var comment : commentsPartContents.getComment()) { var commentWrapper = allComments.get(comment.getId()); - if (commentWrapper != null) { + if (commentWrapper != null) commentWrapper.setComment(comment); - } } } catch (Docx4JException e) { throw new IllegalStateException(e); From 6b2de8c32edce4bb323df2679dc3b41f53feefb9 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 4 May 2024 20:53:53 +0800 Subject: [PATCH 091/134] Add default constructor to OfficeStamperException This commit includes a new default constructor for the OfficeStamperException class. This constructor provides a generic error message "Unexpected exception". Further, it improves code readability by updating formatting for other existing constructors. --- .../api/OfficeStamperException.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/api/OfficeStamperException.java b/src/main/java/pro/verron/docxstamper/api/OfficeStamperException.java index b18ba383..6419d24d 100644 --- a/src/main/java/pro/verron/docxstamper/api/OfficeStamperException.java +++ b/src/main/java/pro/verron/docxstamper/api/OfficeStamperException.java @@ -11,31 +11,38 @@ public class OfficeStamperException /** * OfficeStamperException is a subclass of RuntimeException that represents an exception that can be thrown * during the processing of an Office document using the OfficeStamper - * library. + * library. * * @param message a message describing the error */ - public OfficeStamperException(String message) {super(message);} + public OfficeStamperException(String message) { + super(message); + } /** * OfficeStamperException is a subclass of RuntimeException that represents an exception that can be thrown * during the processing of an Office document using the OfficeStamper - * library. + * library. * * @param cause the cause of the exception */ - public OfficeStamperException(Throwable cause) {super(cause);} + public OfficeStamperException(Throwable cause) { + super(cause); + } /** * OfficeStamperException is a subclass of RuntimeException that represents an exception that can be thrown * during the processing of an Office document using the OfficeStamper - * library. + * library. * * @param message a message describing the error * @param cause the cause of the exception */ public OfficeStamperException(String message, Throwable cause) { - super(message, - cause); + super(message, cause); + } + + public OfficeStamperException() { + super("Unexpected exception"); } } From 1ffc8dd86d7654ab5736c89abcdf53ea3ff16190 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 4 May 2024 21:06:27 +0800 Subject: [PATCH 092/134] Refactor StandardComment and CommentUtil classes The code changes optimize the way the StandardComment class handles the creation of subword documents, removes comment anchors from final elements, throws exceptions and extracts sub-comments from commentChildren. Moving the method removeCommentAnchorsFromFinalElements from StandardComment to CommentUtil contributes to making the classes more concise. The references to repeatElements have been replaced with the term elements to avoid confusion, and exception handling has been improved by the introduction of the OfficeStamperException. --- .../verron/docxstamper/core/CommentUtil.java | 21 ++-- .../docxstamper/core/StandardComment.java | 101 ++++-------------- 2 files changed, 36 insertions(+), 86 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java index c7aa1bfe..6a585e60 100644 --- a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java +++ b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java @@ -262,20 +262,20 @@ public static void deleteCommentFromElement( for (Object item : items) { Object unwrapped = unwrap(item); if (unwrapped instanceof CommentRangeStart crs) { - if (crs.getId() - .equals(commentId)) { + var id = crs.getId(); + if (id.equals(commentId)) { elementsToRemove.add(item); } } else if (unwrapped instanceof CommentRangeEnd cre) { - if (cre.getId() - .equals(commentId)) { + var id = cre.getId(); + if (id.equals(commentId)) { elementsToRemove.add(item); } } else if (unwrapped instanceof R.CommentReference rcr) { - if (rcr.getId() - .equals(commentId)) { + var id = rcr.getId(); + if (id.equals(commentId)) { elementsToRemove.add(item); } } @@ -403,4 +403,13 @@ private static void collectComments( throw new IllegalStateException(e); } } + + static void removeCommentAnchorsFromFinalElements( + Comment comment, + List elements + ) { + var docx4jComment = comment.getComment(); + var commentId = docx4jComment.getId(); + deleteCommentFromElement(elements, commentId); + } } diff --git a/src/main/java/pro/verron/docxstamper/core/StandardComment.java b/src/main/java/pro/verron/docxstamper/core/StandardComment.java index ad5e5583..8bf606bc 100644 --- a/src/main/java/pro/verron/docxstamper/core/StandardComment.java +++ b/src/main/java/pro/verron/docxstamper/core/StandardComment.java @@ -2,15 +2,16 @@ import org.docx4j.XmlUtils; import org.docx4j.jaxb.Context; +import org.docx4j.openpackaging.exceptions.InvalidFormatException; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.openpackaging.parts.WordprocessingML.CommentsPart; import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart; import org.docx4j.wml.*; import org.docx4j.wml.R.CommentReference; import org.jvnet.jaxb2_commons.ppp.Child; -import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.util.DocumentUtil; import pro.verron.docxstamper.api.Comment; +import pro.verron.docxstamper.api.OfficeStamperException; import java.util.*; import java.util.stream.Collectors; @@ -53,10 +54,7 @@ public WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) return createSubWordDocument(this, repeatElements); } - private static WordprocessingMLPackage createSubWordDocument( - Comment comment, - List repeatElements - ) + private static WordprocessingMLPackage createSubWordDocument(Comment comment, List elements) throws InvalidFormatException { WordprocessingMLPackage target = WordprocessingMLPackage.createPackage(); MainDocumentPart targetMainPart = target.getMainDocumentPart(); @@ -65,10 +63,10 @@ private static WordprocessingMLPackage createSubWordDocument( targetMainPart.addTargetPart(commentsPart); // copy the elements to repeat without comment range anchors - List finalRepeatElements = repeatElements.stream() - .map(XmlUtils::deepCopy) - .collect(Collectors.toList()); - StandardComment.removeCommentAnchorsFromFinalElements(comment, finalRepeatElements); + List finalRepeatElements = elements.stream() + .map(XmlUtils::deepCopy) + .collect(Collectors.toList()); + CommentUtil.removeCommentAnchorsFromFinalElements(comment, finalRepeatElements); targetMainPart.getContent() .addAll(finalRepeatElements); @@ -76,7 +74,7 @@ private static WordprocessingMLPackage createSubWordDocument( ObjectFactory wmlObjectFactory = Context.getWmlObjectFactory(); ContentAccessor fakeBody = wmlObjectFactory.createBody(); fakeBody.getContent() - .addAll(repeatElements); + .addAll(elements); DocumentUtil.walkObjectsAndImportImages(fakeBody, comment.getDocument(), target); Comments comments = wmlObjectFactory.createComments(); @@ -85,16 +83,6 @@ private static WordprocessingMLPackage createSubWordDocument( return target; } - private static void removeCommentAnchorsFromFinalElements( - Comment comment, - List finalRepeatElements - ) { - ContentAccessor fakeBody = () -> finalRepeatElements; - CommentUtil.deleteCommentFromElement(fakeBody.getContent(), - comment.getComment() - .getId()); - } - /** *

getParent.

* @@ -131,52 +119,23 @@ public List getElements() { return repeatElements; } - /** - * Creates a new document containing only the elements between the comment range anchors. - * - * @param document the document from which to copy the elements. - * - * @return a new document containing only the elements between the comment range anchors. - * - * @throws Exception if the sub template could not be created. - */ - @Override - public WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) - throws Exception { - List repeatElements = getRepeatElements(); - - WordprocessingMLPackage subDocument = WordprocessingMLPackage.createPackage(); - MainDocumentPart subDocumentMainDocumentPart = subDocument.getMainDocumentPart(); - - CommentsPart commentsPart = new CommentsPart(); - subDocumentMainDocumentPart.addTargetPart(commentsPart); - - // copy the elements to repeat without comment range anchors - List finalRepeatElements = repeatElements.stream() - .map(XmlUtils::deepCopy) - .collect(Collectors.toList()); - removeCommentAnchorsFromFinalElements(finalRepeatElements); - subDocumentMainDocumentPart.getContent() - .addAll(finalRepeatElements); - - // copy the images from parent document using the original repeat elements - ObjectFactory wmlObjectFactory = Context.getWmlObjectFactory(); - ContentAccessor fakeBody = wmlObjectFactory.createBody(); - fakeBody.getContent() - .addAll(repeatElements); - DocumentUtil.walkObjectsAndImportImages(fakeBody, document, subDocument); - - Comments comments = wmlObjectFactory.createComments(); - extractedSubComments(comments.getComment(), this.getChildren()); - commentsPart.setContents(comments); - - return subDocument; + private static void extractedSubComments( + List commentList, + Set commentChildren + ) { + Queue q = new ArrayDeque<>(commentChildren); + while (!q.isEmpty()) { + Comment element = q.remove(); + commentList.add(element.getComment()); + if (element.getChildren() != null) + q.addAll(element.getChildren()); + } } /** * Creates a new document containing only the elements between the comment range anchors. * If the sub template could not be created, a - * {@link DocxStamperException} is thrown. + * {@link OfficeStamperException} is thrown. * * @param document the document from which to copy the elements. * @@ -189,25 +148,7 @@ public WordprocessingMLPackage tryBuildingSubtemplate( try { return getSubTemplate(document); } catch (Exception e) { - throw new DocxStamperException(e); - } - } - - private void removeCommentAnchorsFromFinalElements(List finalRepeatElements) { - ContentAccessor fakeBody = () -> finalRepeatElements; - CommentUtil.deleteCommentFromElement(fakeBody.getContent(), getComment().getId()); - } - - private void extractedSubComments( - List commentList, - Set commentChildren - ) { - Queue q = new ArrayDeque<>(commentChildren); - while (!q.isEmpty()) { - Comment element = q.remove(); - commentList.add(element.getComment()); - if (element.getChildren() != null) - q.addAll(element.getChildren()); + throw new OfficeStamperException(e); } } From f2ed821d7432ec362a7f4bdac670cfb623bd7bcf Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 4 May 2024 21:09:04 +0800 Subject: [PATCH 093/134] Refactor StandardComment class methods Reordered methods in StandardComment class for better readability. The `getSubTemplate` method now directly calls `createSubWordDocument` which gets elements from each Comment. In addition, `extractedSubComments` moved up to improve the logical structure. --- .../java/pro/verron/docxstamper/core/StandardComment.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/core/StandardComment.java b/src/main/java/pro/verron/docxstamper/core/StandardComment.java index 8bf606bc..b69f6b6b 100644 --- a/src/main/java/pro/verron/docxstamper/core/StandardComment.java +++ b/src/main/java/pro/verron/docxstamper/core/StandardComment.java @@ -50,12 +50,13 @@ public StandardComment(WordprocessingMLPackage document) { @Override public WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) throws Exception { - List repeatElements = getElements(); - return createSubWordDocument(this, repeatElements); + return createSubWordDocument(this); } - private static WordprocessingMLPackage createSubWordDocument(Comment comment, List elements) + private static WordprocessingMLPackage createSubWordDocument(Comment comment) throws InvalidFormatException { + var elements = comment.getElements(); + WordprocessingMLPackage target = WordprocessingMLPackage.createPackage(); MainDocumentPart targetMainPart = target.getMainDocumentPart(); From d6b84563dd1ba09e29a71c13f704acdb88a30271 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 4 May 2024 21:18:03 +0800 Subject: [PATCH 094/134] Refactor method name in CommentUtil and relevant calls Updates the method "deleteCommentFromElement" in CommentUtil to "deleteCommentFromElements" to more accurately reflect its functionality. All related calls in StandardComment.java, ParagraphRepeatProcessor.java, RepeatProcessor.java, and within CommentUtil.java are updated accordingly. --- .../processor/repeat/ParagraphRepeatProcessor.java | 2 +- .../docxstamper/processor/repeat/RepeatProcessor.java | 2 +- .../java/pro/verron/docxstamper/core/CommentUtil.java | 8 ++++---- .../java/pro/verron/docxstamper/core/StandardComment.java | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java index f66f4d76..99d49d03 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java @@ -218,7 +218,7 @@ private Deque

generateParagraphsToAdd( pClone); } - CommentUtil.deleteCommentFromElement(pClone.getContent(), + CommentUtil.deleteCommentFromElements(pClone.getContent(), paragraphs.comment.getComment() .getId()); placeholderReplacer.resolveExpressionsForParagraph( diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java index 3b298389..6244151b 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java @@ -109,7 +109,7 @@ private void repeatRows(final WordprocessingMLPackage document) { tableRowsCommentsToRemove.get(row)); Comments.Comment comment = Objects.requireNonNull(commentWrapper.getComment()); BigInteger commentId = comment.getId(); - CommentUtil.deleteCommentFromElement(rowClone.getContent(), commentId); + CommentUtil.deleteCommentFromElements(rowClone.getContent(), commentId); new ParagraphResolverDocumentWalker(rowClone, expressionContext, document, diff --git a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java index 6a585e60..b69c8068 100644 --- a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java +++ b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java @@ -255,7 +255,7 @@ public static Map getComments( * @param items a {@link List} object * @param commentId a {@link BigInteger} object */ - public static void deleteCommentFromElement( + public static void deleteCommentFromElements( List items, BigInteger commentId ) { List elementsToRemove = new ArrayList<>(); @@ -280,7 +280,7 @@ else if (unwrapped instanceof R.CommentReference rcr) { } } else if (unwrapped instanceof ContentAccessor ca) { - deleteCommentFromElement(ca.getContent(), commentId); + deleteCommentFromElements(ca.getContent(), commentId); } } items.removeAll(elementsToRemove); @@ -404,12 +404,12 @@ private static void collectComments( } } - static void removeCommentAnchorsFromFinalElements( + static void deleteCommentFromElements( Comment comment, List elements ) { var docx4jComment = comment.getComment(); var commentId = docx4jComment.getId(); - deleteCommentFromElement(elements, commentId); + deleteCommentFromElements(elements, commentId); } } diff --git a/src/main/java/pro/verron/docxstamper/core/StandardComment.java b/src/main/java/pro/verron/docxstamper/core/StandardComment.java index b69f6b6b..66098727 100644 --- a/src/main/java/pro/verron/docxstamper/core/StandardComment.java +++ b/src/main/java/pro/verron/docxstamper/core/StandardComment.java @@ -67,7 +67,7 @@ private static WordprocessingMLPackage createSubWordDocument(Comment comment) List finalRepeatElements = elements.stream() .map(XmlUtils::deepCopy) .collect(Collectors.toList()); - CommentUtil.removeCommentAnchorsFromFinalElements(comment, finalRepeatElements); + CommentUtil.deleteCommentFromElements(comment, finalRepeatElements); targetMainPart.getContent() .addAll(finalRepeatElements); From d8198d7829307232dbf93dc54b87ad893b431910 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 4 May 2024 21:19:01 +0800 Subject: [PATCH 095/134] Mark newInstanceWithNullReplacement method as deprecated This commit marks the newInstanceWithNullReplacement method in RepeatProcessor as deprecated since it is unused in core library. This method will be removed in future updates. --- .../docxstamper/processor/repeat/RepeatProcessor.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java index 6244151b..e07beec3 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java @@ -44,7 +44,9 @@ private RepeatProcessor( * * @param pr The PlaceholderReplacer to use. * @return A new RepeatProcessor. + * @deprecated since unused in core lib */ + @Deprecated(since = "1.6.8", forRemoval = true) public static CommentProcessor newInstanceWithNullReplacement( PlaceholderReplacer pr ) { From 636bbcce9421d407a67d585e1735b63a9a218918 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 4 May 2024 21:42:30 +0800 Subject: [PATCH 096/134] Refactor StandardComment in docxstamper This commit rearranges and slightly modifies the methods in the StandardComment class. Refactored the "getSubTemplate" and "createSubWordDocument" functions for a clearer and optimized code structure. A re-usable "extractComments" function was also created to avoid repeated code. --- .../docxstamper/core/StandardComment.java | 117 +++++++++--------- 1 file changed, 59 insertions(+), 58 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/core/StandardComment.java b/src/main/java/pro/verron/docxstamper/core/StandardComment.java index 66098727..00319c2c 100644 --- a/src/main/java/pro/verron/docxstamper/core/StandardComment.java +++ b/src/main/java/pro/verron/docxstamper/core/StandardComment.java @@ -5,7 +5,6 @@ import org.docx4j.openpackaging.exceptions.InvalidFormatException; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.openpackaging.parts.WordprocessingML.CommentsPart; -import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart; import org.docx4j.wml.*; import org.docx4j.wml.R.CommentReference; import org.jvnet.jaxb2_commons.ppp.Child; @@ -38,52 +37,6 @@ public StandardComment(WordprocessingMLPackage document) { this.document = document; } - /** - * Creates a new document containing only the elements between the comment range anchors. - * - * @param document the document from which to copy the elements. - * - * @return a new document containing only the elements between the comment range anchors. - * - * @throws Exception if the sub template could not be created. - */ - @Override - public WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) - throws Exception { - return createSubWordDocument(this); - } - - private static WordprocessingMLPackage createSubWordDocument(Comment comment) - throws InvalidFormatException { - var elements = comment.getElements(); - - WordprocessingMLPackage target = WordprocessingMLPackage.createPackage(); - MainDocumentPart targetMainPart = target.getMainDocumentPart(); - - CommentsPart commentsPart = new CommentsPart(); - targetMainPart.addTargetPart(commentsPart); - - // copy the elements to repeat without comment range anchors - List finalRepeatElements = elements.stream() - .map(XmlUtils::deepCopy) - .collect(Collectors.toList()); - CommentUtil.deleteCommentFromElements(comment, finalRepeatElements); - targetMainPart.getContent() - .addAll(finalRepeatElements); - - // copy the images from parent document using the original repeat elements - ObjectFactory wmlObjectFactory = Context.getWmlObjectFactory(); - ContentAccessor fakeBody = wmlObjectFactory.createBody(); - fakeBody.getContent() - .addAll(elements); - DocumentUtil.walkObjectsAndImportImages(fakeBody, comment.getDocument(), target); - - Comments comments = wmlObjectFactory.createComments(); - StandardComment.extractedSubComments(comments.getComment(), comment.getChildren()); - commentsPart.setContents(comments); - return target; - } - /** *

getParent.

* @@ -120,17 +73,19 @@ public List getElements() { return repeatElements; } - private static void extractedSubComments( - List commentList, - Set commentChildren - ) { - Queue q = new ArrayDeque<>(commentChildren); - while (!q.isEmpty()) { - Comment element = q.remove(); - commentList.add(element.getComment()); - if (element.getChildren() != null) - q.addAll(element.getChildren()); - } + /** + * Creates a new document containing only the elements between the comment range anchors. + * + * @param document the document from which to copy the elements. + * + * @return a new document containing only the elements between the comment range anchors. + * + * @throws Exception if the sub template could not be created. + */ + @Override + public WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) + throws Exception { + return createSubWordDocument(this); } /** @@ -153,6 +108,52 @@ public WordprocessingMLPackage tryBuildingSubtemplate( } } + private static WordprocessingMLPackage createSubWordDocument(Comment comment) + throws InvalidFormatException { + var elements = comment.getElements(); + + var targetCommentsPart = new CommentsPart(); + + var target = WordprocessingMLPackage.createPackage(); + var targetMainPart = target.getMainDocumentPart(); + targetMainPart.addTargetPart(targetCommentsPart); + + // copy the elements without comment range anchors + var finalElements = elements.stream() + .map(XmlUtils::deepCopy) + .collect(Collectors.toList()); + CommentUtil.deleteCommentFromElements(comment, finalElements); + targetMainPart.getContent() + .addAll(finalElements); + + // copy the images from parent document using the original repeat elements + var wmlObjectFactory = Context.getWmlObjectFactory(); + var fakeBody = wmlObjectFactory.createBody(); + fakeBody.getContent() + .addAll(elements); + DocumentUtil.walkObjectsAndImportImages(fakeBody, comment.getDocument(), target); + + var comments = StandardComment.extractComments(comment.getChildren()); + targetCommentsPart.setContents(comments); + return target; + } + + private static Comments extractComments(Set commentChildren) { + var wmlObjectFactory = Context.getWmlObjectFactory(); + var comments = wmlObjectFactory.createComments(); + var commentList = comments.getComment(); + + var queue = new ArrayDeque<>(commentChildren); + while (!queue.isEmpty()) { + var comment = queue.remove(); + commentList.add(comment.getComment()); + if (comment.getChildren() != null) { + queue.addAll(comment.getChildren()); + } + } + return comments; + } + /** *

Getter for the field commentRangeEnd.

* From 5909e1f019f98a4fdc79fa6a10a8de43c0cb4c6c Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 4 May 2024 21:57:43 +0800 Subject: [PATCH 097/134] Refactor StandardComment for better exception handling The methods 'findGreatestCommonParent' and 'findInsertableParent' within 'StandardComment' have been refactored. This refactoring includes modifying the input parameters and improving the exception handling by throwing an 'OfficeStamperException' when unexpected types are encountered. The change increases the robustness of the code. --- .../docxstamper/core/StandardComment.java | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/core/StandardComment.java b/src/main/java/pro/verron/docxstamper/core/StandardComment.java index 00319c2c..8edacb2d 100644 --- a/src/main/java/pro/verron/docxstamper/core/StandardComment.java +++ b/src/main/java/pro/verron/docxstamper/core/StandardComment.java @@ -45,8 +45,8 @@ public StandardComment(WordprocessingMLPackage document) { @Override public ContentAccessor getParent() { return findGreatestCommonParent( - getCommentRangeEnd().getParent(), - (ContentAccessor) getCommentRangeStart().getParent() + getCommentRangeStart(), + getCommentRangeEnd() ); } @@ -228,11 +228,13 @@ public void setComment(Comments.Comment comment) { return document; } - private ContentAccessor findGreatestCommonParent(Object end, ContentAccessor start) { - if (depthElementSearch(end, start)) { - return findInsertableParent(start); - } - return findGreatestCommonParent(end, (ContentAccessor) ((Child) start).getParent()); + private ContentAccessor findGreatestCommonParent(Object o1, Object o2) { + if (depthElementSearch(o1, o2) && o2 instanceof ContentAccessor contentAccessor) + return findInsertableParent(contentAccessor); + else if (o2 instanceof Child child) + return findGreatestCommonParent(o1, child.getParent()); + else + throw new OfficeStamperException(); } private boolean depthElementSearch(Object searchTarget, Object content) { @@ -252,11 +254,15 @@ else if (content instanceof ContentAccessor contentAccessor) { return false; } - private ContentAccessor findInsertableParent(ContentAccessor searchFrom) { - if (!(searchFrom instanceof Tc || searchFrom instanceof Body)) { - return findInsertableParent((ContentAccessor) ((Child) searchFrom).getParent()); - } - return searchFrom; + private ContentAccessor findInsertableParent(Object searchFrom) { + if (searchFrom instanceof Tc tc) + return tc; + else if (searchFrom instanceof Body body) + return body; + else if (searchFrom instanceof Child child) + return findInsertableParent(child.getParent()); + else + throw new OfficeStamperException(); } } From 697628277ddacbd321634bcc00eb3d69d494058b Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 4 May 2024 22:03:39 +0800 Subject: [PATCH 098/134] Refactor method to find smallest common parent The method findGreatestCommonParent has been renamed to findSmallestCommonParent for better accuracy in terms of functionality. Correspondingly, its usages in the other parts of the code have also been updated. --- .../pro/verron/docxstamper/core/StandardComment.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/core/StandardComment.java b/src/main/java/pro/verron/docxstamper/core/StandardComment.java index 8edacb2d..12dce386 100644 --- a/src/main/java/pro/verron/docxstamper/core/StandardComment.java +++ b/src/main/java/pro/verron/docxstamper/core/StandardComment.java @@ -44,10 +44,7 @@ public StandardComment(WordprocessingMLPackage document) { */ @Override public ContentAccessor getParent() { - return findGreatestCommonParent( - getCommentRangeStart(), - getCommentRangeEnd() - ); + return findSmallestCommonParent(getCommentRangeStart(), getCommentRangeEnd()); } /** @@ -228,11 +225,11 @@ public void setComment(Comments.Comment comment) { return document; } - private ContentAccessor findGreatestCommonParent(Object o1, Object o2) { + private ContentAccessor findSmallestCommonParent(Object o1, Object o2) { if (depthElementSearch(o1, o2) && o2 instanceof ContentAccessor contentAccessor) return findInsertableParent(contentAccessor); else if (o2 instanceof Child child) - return findGreatestCommonParent(o1, child.getParent()); + return findSmallestCommonParent(o1, child.getParent()); else throw new OfficeStamperException(); } From 71f661a107deaf5d3dd2228557ff2e4e3570b618 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 4 May 2024 22:06:37 +0800 Subject: [PATCH 099/134] Refactor code by moving methods to DocumentUtil class The findSmallestCommonParent, depthElementSearch, and findInsertableParent methods have been refactored from the StandardComment class to the DocumentUtil class. This change centralizes these utility methods, making the code cleaner and more manageable. --- .../docxstamper/util/DocumentUtil.java | 39 +++++++++++++++ .../docxstamper/core/StandardComment.java | 49 +++---------------- 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/util/DocumentUtil.java b/src/main/java/org/wickedsource/docxstamper/util/DocumentUtil.java index e275c092..0cc9e5d3 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/DocumentUtil.java +++ b/src/main/java/org/wickedsource/docxstamper/util/DocumentUtil.java @@ -2,12 +2,14 @@ import jakarta.xml.bind.JAXBElement; import org.docx4j.TraversalUtil; +import org.docx4j.XmlUtils; import org.docx4j.finders.ClassFinder; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage; import org.docx4j.openpackaging.parts.relationships.Namespaces; import org.docx4j.openpackaging.parts.relationships.RelationshipsPart; import org.docx4j.wml.*; +import org.jvnet.jaxb2_commons.ppp.Child; import pro.verron.docxstamper.api.OfficeStamperException; import java.util.*; @@ -264,4 +266,41 @@ private static BinaryPartAbstractImage tryCreateImagePart( public static Stream

streamParagraphs(WordprocessingMLPackage document) { return streamElements(document, P.class); } + + public static ContentAccessor findSmallestCommonParent(Object o1, Object o2) { + if (depthElementSearch(o1, o2) && o2 instanceof ContentAccessor contentAccessor) + return findInsertableParent(contentAccessor); + else if (o2 instanceof Child child) + return findSmallestCommonParent(o1, child.getParent()); + else + throw new OfficeStamperException(); + } + + public static boolean depthElementSearch(Object searchTarget, Object content) { + content = XmlUtils.unwrap(content); + if (searchTarget.equals(content)) { + return true; + } + else if (content instanceof ContentAccessor contentAccessor) { + for (Object object : contentAccessor.getContent()) { + Object unwrappedObject = XmlUtils.unwrap(object); + if (searchTarget.equals(unwrappedObject) + || depthElementSearch(searchTarget, unwrappedObject)) { + return true; + } + } + } + return false; + } + + private static ContentAccessor findInsertableParent(Object searchFrom) { + if (searchFrom instanceof Tc tc) + return tc; + else if (searchFrom instanceof Body body) + return body; + else if (searchFrom instanceof Child child) + return findInsertableParent(child.getParent()); + else + throw new OfficeStamperException(); + } } diff --git a/src/main/java/pro/verron/docxstamper/core/StandardComment.java b/src/main/java/pro/verron/docxstamper/core/StandardComment.java index 12dce386..88fedeef 100644 --- a/src/main/java/pro/verron/docxstamper/core/StandardComment.java +++ b/src/main/java/pro/verron/docxstamper/core/StandardComment.java @@ -5,9 +5,11 @@ import org.docx4j.openpackaging.exceptions.InvalidFormatException; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.openpackaging.parts.WordprocessingML.CommentsPart; -import org.docx4j.wml.*; +import org.docx4j.wml.CommentRangeEnd; +import org.docx4j.wml.CommentRangeStart; +import org.docx4j.wml.Comments; +import org.docx4j.wml.ContentAccessor; import org.docx4j.wml.R.CommentReference; -import org.jvnet.jaxb2_commons.ppp.Child; import org.wickedsource.docxstamper.util.DocumentUtil; import pro.verron.docxstamper.api.Comment; import pro.verron.docxstamper.api.OfficeStamperException; @@ -44,7 +46,7 @@ public StandardComment(WordprocessingMLPackage document) { */ @Override public ContentAccessor getParent() { - return findSmallestCommonParent(getCommentRangeStart(), getCommentRangeEnd()); + return DocumentUtil.findSmallestCommonParent(getCommentRangeStart(), getCommentRangeEnd()); } /** @@ -57,12 +59,12 @@ public List getElements() { List repeatElements = new ArrayList<>(); boolean startFound = false; for (Object element : getParent().getContent()) { - if (!startFound && depthElementSearch(getCommentRangeStart(), element)) { + if (!startFound && DocumentUtil.depthElementSearch(getCommentRangeStart(), element)) { startFound = true; } if (startFound) { repeatElements.add(element); - if (depthElementSearch(getCommentRangeEnd(), element)) { + if (DocumentUtil.depthElementSearch(getCommentRangeEnd(), element)) { break; } } @@ -225,41 +227,4 @@ public void setComment(Comments.Comment comment) { return document; } - private ContentAccessor findSmallestCommonParent(Object o1, Object o2) { - if (depthElementSearch(o1, o2) && o2 instanceof ContentAccessor contentAccessor) - return findInsertableParent(contentAccessor); - else if (o2 instanceof Child child) - return findSmallestCommonParent(o1, child.getParent()); - else - throw new OfficeStamperException(); - } - - private boolean depthElementSearch(Object searchTarget, Object content) { - content = XmlUtils.unwrap(content); - if (searchTarget.equals(content)) { - return true; - } - else if (content instanceof ContentAccessor contentAccessor) { - for (Object object : contentAccessor.getContent()) { - Object unwrappedObject = XmlUtils.unwrap(object); - if (searchTarget.equals(unwrappedObject) - || depthElementSearch(searchTarget, unwrappedObject)) { - return true; - } - } - } - return false; - } - - private ContentAccessor findInsertableParent(Object searchFrom) { - if (searchFrom instanceof Tc tc) - return tc; - else if (searchFrom instanceof Body body) - return body; - else if (searchFrom instanceof Child child) - return findInsertableParent(child.getParent()); - else - throw new OfficeStamperException(); - } - } From 139cbcaaea0510c33acbb86506f1ab149957215c Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 4 May 2024 22:30:02 +0800 Subject: [PATCH 100/134] Refactor getElements method in StandardComment class Simplified the getElements method logic by reducing the number of conditions inside the loop and renaming the local list for better readability. The modification should make the function easier to understand and maintain. --- .../docxstamper/core/StandardComment.java | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/core/StandardComment.java b/src/main/java/pro/verron/docxstamper/core/StandardComment.java index 88fedeef..e0c745c9 100644 --- a/src/main/java/pro/verron/docxstamper/core/StandardComment.java +++ b/src/main/java/pro/verron/docxstamper/core/StandardComment.java @@ -50,26 +50,20 @@ public ContentAccessor getParent() { } /** - *

getRepeatElements.

- * * @return the elements in the document that are between the comment range anchors. */ @Override public List getElements() { - List repeatElements = new ArrayList<>(); + List elements = new ArrayList<>(); boolean startFound = false; - for (Object element : getParent().getContent()) { - if (!startFound && DocumentUtil.depthElementSearch(getCommentRangeStart(), element)) { - startFound = true; - } - if (startFound) { - repeatElements.add(element); - if (DocumentUtil.depthElementSearch(getCommentRangeEnd(), element)) { - break; - } - } + boolean endFound = false; + var parentElements = getParent().getContent(); + for (Object element : parentElements) { + startFound = startFound || DocumentUtil.depthElementSearch(getCommentRangeStart(), element); + if (startFound && !endFound) elements.add(element); + endFound = endFound || DocumentUtil.depthElementSearch(getCommentRangeEnd(), element); } - return repeatElements; + return elements; } /** From 05d83c3e232a2f9d02e41474bf2b67b1c519c34b Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 4 May 2024 22:31:39 +0800 Subject: [PATCH 101/134] Add default constructor to CommentWrapper A default constructor has been added to the CommentWrapper class for compatibility reasons. Note that this class has been deprecated, and using this constructor is not recommended. --- .../java/org/wickedsource/docxstamper/util/CommentWrapper.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/wickedsource/docxstamper/util/CommentWrapper.java b/src/main/java/org/wickedsource/docxstamper/util/CommentWrapper.java index 70cade76..484b8dfc 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/CommentWrapper.java +++ b/src/main/java/org/wickedsource/docxstamper/util/CommentWrapper.java @@ -14,4 +14,7 @@ @Deprecated(since = "1.6.8", forRemoval = true) public class CommentWrapper extends StandardComment { + public CommentWrapper() { + super(null); + } } From 436b0a92bb7fa1d9753b8674ea727cd912bc443b Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 4 May 2024 22:33:41 +0800 Subject: [PATCH 102/134] Refactor code to move utility functions to CommentUtil Codebase cleanup was performed where createSubWordDocument and extractComments methods were migrated from StandardComment class to the CommentUtil class. The migration promotes code reusability and enhances maintenance by separating utility functions from the main class. --- .../verron/docxstamper/core/CommentUtil.java | 49 ++++++++++++++++ .../docxstamper/core/StandardComment.java | 58 ++----------------- 2 files changed, 54 insertions(+), 53 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java index b69c8068..d7a34e28 100644 --- a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java +++ b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java @@ -1,6 +1,8 @@ package pro.verron.docxstamper.core; import org.docx4j.TextUtils; +import org.docx4j.XmlUtils; +import org.docx4j.jaxb.Context; import org.docx4j.openpackaging.exceptions.Docx4JException; import org.docx4j.openpackaging.exceptions.InvalidFormatException; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; @@ -10,6 +12,7 @@ import org.jvnet.jaxb2_commons.ppp.Child; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.wickedsource.docxstamper.util.DocumentUtil; import org.wickedsource.docxstamper.util.walk.BaseDocumentWalker; import org.wickedsource.docxstamper.util.walk.DocumentWalker; import pro.verron.docxstamper.api.Comment; @@ -412,4 +415,50 @@ static void deleteCommentFromElements( var commentId = docx4jComment.getId(); deleteCommentFromElements(elements, commentId); } + + static WordprocessingMLPackage createSubWordDocument(Comment comment) + throws InvalidFormatException { + var elements = comment.getElements(); + + var targetCommentsPart = new CommentsPart(); + + var target = WordprocessingMLPackage.createPackage(); + var targetMainPart = target.getMainDocumentPart(); + targetMainPart.addTargetPart(targetCommentsPart); + + // copy the elements without comment range anchors + var finalElements = elements.stream() + .map(XmlUtils::deepCopy) + .collect(Collectors.toList()); + deleteCommentFromElements(comment, finalElements); + targetMainPart.getContent() + .addAll(finalElements); + + // copy the images from parent document using the original repeat elements + var wmlObjectFactory = Context.getWmlObjectFactory(); + var fakeBody = wmlObjectFactory.createBody(); + fakeBody.getContent() + .addAll(elements); + DocumentUtil.walkObjectsAndImportImages(fakeBody, comment.getDocument(), target); + + var comments = extractComments(comment.getChildren()); + targetCommentsPart.setContents(comments); + return target; + } + + private static Comments extractComments(Set commentChildren) { + var wmlObjectFactory = Context.getWmlObjectFactory(); + var comments = wmlObjectFactory.createComments(); + var commentList = comments.getComment(); + + var queue = new ArrayDeque<>(commentChildren); + while (!queue.isEmpty()) { + var comment = queue.remove(); + commentList.add(comment.getComment()); + if (comment.getChildren() != null) { + queue.addAll(comment.getChildren()); + } + } + return comments; + } } diff --git a/src/main/java/pro/verron/docxstamper/core/StandardComment.java b/src/main/java/pro/verron/docxstamper/core/StandardComment.java index e0c745c9..6baf7035 100644 --- a/src/main/java/pro/verron/docxstamper/core/StandardComment.java +++ b/src/main/java/pro/verron/docxstamper/core/StandardComment.java @@ -1,10 +1,6 @@ package pro.verron.docxstamper.core; -import org.docx4j.XmlUtils; -import org.docx4j.jaxb.Context; -import org.docx4j.openpackaging.exceptions.InvalidFormatException; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; -import org.docx4j.openpackaging.parts.WordprocessingML.CommentsPart; import org.docx4j.wml.CommentRangeEnd; import org.docx4j.wml.CommentRangeStart; import org.docx4j.wml.Comments; @@ -14,8 +10,10 @@ import pro.verron.docxstamper.api.Comment; import pro.verron.docxstamper.api.OfficeStamperException; -import java.util.*; -import java.util.stream.Collectors; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** *

CommentWrapper class.

@@ -78,7 +76,7 @@ public List getElements() { @Override public WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) throws Exception { - return createSubWordDocument(this); + return CommentUtil.createSubWordDocument(this); } /** @@ -101,52 +99,6 @@ public WordprocessingMLPackage tryBuildingSubtemplate( } } - private static WordprocessingMLPackage createSubWordDocument(Comment comment) - throws InvalidFormatException { - var elements = comment.getElements(); - - var targetCommentsPart = new CommentsPart(); - - var target = WordprocessingMLPackage.createPackage(); - var targetMainPart = target.getMainDocumentPart(); - targetMainPart.addTargetPart(targetCommentsPart); - - // copy the elements without comment range anchors - var finalElements = elements.stream() - .map(XmlUtils::deepCopy) - .collect(Collectors.toList()); - CommentUtil.deleteCommentFromElements(comment, finalElements); - targetMainPart.getContent() - .addAll(finalElements); - - // copy the images from parent document using the original repeat elements - var wmlObjectFactory = Context.getWmlObjectFactory(); - var fakeBody = wmlObjectFactory.createBody(); - fakeBody.getContent() - .addAll(elements); - DocumentUtil.walkObjectsAndImportImages(fakeBody, comment.getDocument(), target); - - var comments = StandardComment.extractComments(comment.getChildren()); - targetCommentsPart.setContents(comments); - return target; - } - - private static Comments extractComments(Set commentChildren) { - var wmlObjectFactory = Context.getWmlObjectFactory(); - var comments = wmlObjectFactory.createComments(); - var commentList = comments.getComment(); - - var queue = new ArrayDeque<>(commentChildren); - while (!queue.isEmpty()) { - var comment = queue.remove(); - commentList.add(comment.getComment()); - if (comment.getChildren() != null) { - queue.addAll(comment.getChildren()); - } - } - return comments; - } - /** *

Getter for the field commentRangeEnd.

* From 2783fd1001fc5b59ea0ed4cb4758297cb0b20cb9 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sat, 4 May 2024 22:40:14 +0800 Subject: [PATCH 103/134] Deprecate methods and redirect to CommentUtil class The `getSubTemplate()` and `tryBuildingSubtemplate()` methods in the `Comment.java` and `StandardComment.java` files have been marked as deprecated. These methods have been moved to the `CommentUtil` class to improve code structure and modularity. Developers are advised to use the `CommentUtil#createSubWordDocument(Comment)` method instead. --- src/main/java/pro/verron/docxstamper/api/Comment.java | 7 +++++-- src/main/java/pro/verron/docxstamper/core/CommentUtil.java | 2 +- .../java/pro/verron/docxstamper/core/StandardComment.java | 4 ++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/api/Comment.java b/src/main/java/pro/verron/docxstamper/api/Comment.java index d66f8abc..e285d3a2 100644 --- a/src/main/java/pro/verron/docxstamper/api/Comment.java +++ b/src/main/java/pro/verron/docxstamper/api/Comment.java @@ -2,6 +2,7 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.*; +import pro.verron.docxstamper.core.CommentUtil; import java.util.List; import java.util.Set; @@ -44,8 +45,9 @@ default List getRepeatElements() { * @return a new document containing only the elements between the comment range anchors. * * @throws Exception if the sub template could not be created. + * @deprecated use {@link CommentUtil#createSubWordDocument(Comment)} instead */ - // TODO: Remove from this interface and move to an utility class + @Deprecated(since = "1.6.8", forRemoval = true) WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) throws Exception; @@ -55,8 +57,9 @@ WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) * @param document the source document from which to build the subtemplate * * @return the built subtemplate as a WordprocessingMLPackage + * @deprecated use {@link CommentUtil#createSubWordDocument(Comment)} instead */ - // TODO: Remove from this interface and move to an utility class + @Deprecated(since = "1.6.8", forRemoval = true) WordprocessingMLPackage tryBuildingSubtemplate(WordprocessingMLPackage document); /** diff --git a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java index d7a34e28..4dffe0cd 100644 --- a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java +++ b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java @@ -416,7 +416,7 @@ static void deleteCommentFromElements( deleteCommentFromElements(elements, commentId); } - static WordprocessingMLPackage createSubWordDocument(Comment comment) + public static WordprocessingMLPackage createSubWordDocument(Comment comment) throws InvalidFormatException { var elements = comment.getElements(); diff --git a/src/main/java/pro/verron/docxstamper/core/StandardComment.java b/src/main/java/pro/verron/docxstamper/core/StandardComment.java index 6baf7035..8f00d65d 100644 --- a/src/main/java/pro/verron/docxstamper/core/StandardComment.java +++ b/src/main/java/pro/verron/docxstamper/core/StandardComment.java @@ -72,7 +72,9 @@ public List getElements() { * @return a new document containing only the elements between the comment range anchors. * * @throws Exception if the sub template could not be created. + * @deprecated use {@link CommentUtil#createSubWordDocument(Comment)} instead */ + @Deprecated(since = "1.6.8", forRemoval = true) @Override public WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) throws Exception { @@ -87,7 +89,9 @@ public WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) * @param document the document from which to copy the elements. * * @return a new document containing only the elements between the comment range anchors. + * @deprecated use {@link CommentUtil#createSubWordDocument(Comment)} instead */ + @Deprecated(since = "1.6.8", forRemoval = true) @Override public WordprocessingMLPackage tryBuildingSubtemplate( WordprocessingMLPackage document From 76456f4e957061fc12160d085d9576a77a44409d Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Mon, 6 May 2024 13:03:00 +0800 Subject: [PATCH 104/134] Changed priority of TODOs in Comment.java to TODO_LATER The priority of several TODO comments in the Comment.java file was decreased from TODO to TODO_LATER. This change is meant to reflect that removing the setting methods from the interface to increase immutability might not be an immediate concern. --- src/main/java/pro/verron/docxstamper/api/Comment.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/api/Comment.java b/src/main/java/pro/verron/docxstamper/api/Comment.java index e285d3a2..5b3927ef 100644 --- a/src/main/java/pro/verron/docxstamper/api/Comment.java +++ b/src/main/java/pro/verron/docxstamper/api/Comment.java @@ -74,7 +74,7 @@ WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) * * @param commentRangeEnd the {@link CommentRangeEnd} object to set */ - // TODO: Remove the setting method from interface to increase immutability + // TODO_LATER: Remove the setting method from interface to increase immutability void setCommentRangeEnd(CommentRangeEnd commentRangeEnd); /** @@ -89,7 +89,7 @@ WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) * * @param commentRangeStart the CommentRangeStart object to set */ - // TODO: Remove the setting method from interface to increase immutability + // TODO_LATER: Remove the setting method from interface to increase immutability void setCommentRangeStart(CommentRangeStart commentRangeStart); /** @@ -104,7 +104,7 @@ WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) * * @param commentReference the comment reference to set */ - // TODO: Remove the setting method from interface to increase immutability + // TODO_LATER: Remove the setting method from interface to increase immutability void setCommentReference(R.CommentReference commentReference); /** @@ -119,7 +119,7 @@ WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) * * @param comments the set of Comment objects representing the children of the comment */ - // TODO: Remove the setting method from interface to increase immutability + // TODO_LATER: Remove the setting method from interface to increase immutability void setChildren(Set comments); /** @@ -134,7 +134,7 @@ WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) * * @param comment the comment to set */ - // TODO: Remove the setting method from interface to increase immutability + // TODO_LATER: Remove the setting method from interface to increase immutability void setComment(Comments.Comment comment); WordprocessingMLPackage getDocument(); From 9a2d798ff4d9d2b9a0dbc066570e975c9973f62f Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Mon, 6 May 2024 13:10:21 +0800 Subject: [PATCH 105/134] Add javadoc for getLineBreakPlaceholder method The javadoc comment for the getLineBreakPlaceholder method in OfficeStamperConfiguration has been added. The documentation provides clarification about the method's purpose, specifically, retrieving the line break placeholder used in the OfficeStamper configuration. --- .../verron/docxstamper/api/OfficeStamperConfiguration.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/pro/verron/docxstamper/api/OfficeStamperConfiguration.java b/src/main/java/pro/verron/docxstamper/api/OfficeStamperConfiguration.java index e2bda3cc..235cf4d5 100644 --- a/src/main/java/pro/verron/docxstamper/api/OfficeStamperConfiguration.java +++ b/src/main/java/pro/verron/docxstamper/api/OfficeStamperConfiguration.java @@ -180,7 +180,9 @@ OfficeStamperConfiguration addCommentProcessor( String getUnresolvedExpressionsDefaultValue(); /** - * TODO: javadoc + * Retrieves the line break placeholder used in the OfficeStamper configuration. + * + * @return the line break placeholder as a String. */ String getLineBreakPlaceholder(); From c8b6855954d49752a30816038a0779221846afec Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Mon, 6 May 2024 13:12:13 +0800 Subject: [PATCH 106/134] Added documentation for ExcelStamper and ExcelParagraph classes Added descriptive javadocs for ExcelStamper and ExcelParagraph classes. It details their purpose, behavior and the libraries or methods they use. This addition will provide better understanding for future code maintenance and updates. --- src/main/java/pro/verron/docxstamper/core/ExcelParagraph.java | 3 ++- src/main/java/pro/verron/docxstamper/core/ExcelStamper.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/core/ExcelParagraph.java b/src/main/java/pro/verron/docxstamper/core/ExcelParagraph.java index b5ca0f0d..264975c4 100644 --- a/src/main/java/pro/verron/docxstamper/core/ExcelParagraph.java +++ b/src/main/java/pro/verron/docxstamper/core/ExcelParagraph.java @@ -4,7 +4,8 @@ import pro.verron.docxstamper.api.Placeholder; /** - * TODO: javadoc + * The ExcelParagraph class represents a paragraph in an Excel document. + * It provides methods to replace expressions and retrieve the aggregated text over all runs. */ public class ExcelParagraph { private final CTRst paragraph; diff --git a/src/main/java/pro/verron/docxstamper/core/ExcelStamper.java b/src/main/java/pro/verron/docxstamper/core/ExcelStamper.java index 361e43a8..54fa1721 100644 --- a/src/main/java/pro/verron/docxstamper/core/ExcelStamper.java +++ b/src/main/java/pro/verron/docxstamper/core/ExcelStamper.java @@ -14,7 +14,8 @@ import static pro.verron.docxstamper.core.Placeholders.findVariables; /** - * TODO: javadoc + * The ExcelStamper class is an implementation of the OfficeStamper interface for stamping Excel templates. + * It uses the DOCX4J library to manipulate the template and replace variable expressions with values from the context. */ public class ExcelStamper implements OfficeStamper { From 4041b2c23240d44cbd04faaad170c80c515f2a24 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Mon, 6 May 2024 13:14:24 +0800 Subject: [PATCH 107/134] Updated TODO tags in CommentUtil and ImageResolver Added "_LATER" to TODO comments in CommentUtil and ImageResolver classes, indicating that these tasks should be done in future versions rather than immediately. Both of these changes were made to clarify the priorities of our refactoring tasks. --- src/main/java/pro/verron/docxstamper/core/CommentUtil.java | 2 +- .../pro/verron/docxstamper/preset/resolver/ImageResolver.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java index 4dffe0cd..3f275b0a 100644 --- a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java +++ b/src/main/java/pro/verron/docxstamper/core/CommentUtil.java @@ -41,7 +41,7 @@ public class CommentUtil { /** * Utility class for handling comments in a DOCX document. */ - // TODO: Move to private for next version + // TODO_LATER: Move to private for next version protected CommentUtil() { throw new OfficeStamperException("Utility class shouldn't be instantiated"); } diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java index c7acdc08..7b4c3d1b 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java @@ -33,7 +33,7 @@ public class ImageResolver */ public R resolve(WordprocessingMLPackage document, Image image) { try { - // TODO: adding the same image twice will put the image twice into the docx-zip file. make the second + // TODO_LATER: adding the same image twice will put the image twice into the docx-zip file. make the second // addition of the same image a reference instead. return RunUtil.createRunWithImage( image.getMaxWidth(), From bd78b24f0a01c59c7f11e200f9fc2c6e37595153 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Mon, 6 May 2024 13:14:47 +0800 Subject: [PATCH 108/134] Added a TODO comment to handle unknown case exception A comment has been inserted to remind of the need to replace the test throws with a no-operation (noop) before deploying in a production environment. This is crucial to prevent an OfficeStamperException from breaking the application when encountering unknown cases. --- src/main/java/pro/verron/docxstamper/core/ExcelVisitor.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/pro/verron/docxstamper/core/ExcelVisitor.java b/src/main/java/pro/verron/docxstamper/core/ExcelVisitor.java index 27a9e334..86b5631c 100644 --- a/src/main/java/pro/verron/docxstamper/core/ExcelVisitor.java +++ b/src/main/java/pro/verron/docxstamper/core/ExcelVisitor.java @@ -53,6 +53,7 @@ else if (object instanceof STSheetState ignored) { /* do nothing */ } else if (object instanceof Map element) visit(element.entrySet()); else if (object instanceof Map.Entry element) visit(element.getValue()); else if (object == null) { /* do nothing */ } + // TODO: replace this test throws by a noop before usage in the wild else throw new OfficeStamperException("Unknown case : %s %s".formatted(object, object.getClass())); } catch (Docx4JException e) { throw new OfficeStamperException(e); From 0d36c997e218c6ef660335db6fbb1e941ba935d5 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Mon, 6 May 2024 13:16:07 +0800 Subject: [PATCH 109/134] Refactor PlaceholderReplacer.java for code readability The code in the PlaceholderReplacer.java file has been refactored. Complex method calls have been simplified and unnecessary spaces removed. The function of the code remains unchanged; the refactoring merely makes it more readable, improving managability and reducing the potential for error. --- .../docxstamper/core/PlaceholderReplacer.java | 58 ++++++------------- 1 file changed, 17 insertions(+), 41 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java index 85c57ef2..d0879302 100644 --- a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java +++ b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java @@ -82,16 +82,11 @@ public PlaceholderReplacer( * @param expressionContext the context root */ public void resolveExpressions( - final WordprocessingMLPackage document, - Object expressionContext + final WordprocessingMLPackage document, Object expressionContext ) { new BaseCoordinatesWalker() { - @Override - protected void onParagraph(P paragraph) { - resolveExpressionsForParagraph( - new StandardParagraph(paragraph), - expressionContext, - document); + @Override protected void onParagraph(P paragraph) { + resolveExpressionsForParagraph(new StandardParagraph(paragraph), expressionContext, document); } }.walk(document); } @@ -103,70 +98,51 @@ protected void onParagraph(P paragraph) { * @param context the context root * @param document the document in which to replace all expressions. */ - @Override - public void resolveExpressionsForParagraph( - Paragraph paragraph, - Object context, - WordprocessingMLPackage document + @Override public void resolveExpressionsForParagraph( + Paragraph paragraph, Object context, WordprocessingMLPackage document ) { var expressions = Placeholders.findVariables(paragraph); for (var expression : expressions) { try { var resolution = resolver.resolve(expression, context); - var replacement = registry.resolve(document, expression, - resolution); + var replacement = registry.resolve(document, expression, resolution); paragraph.replace(expression, replacement); } catch (SpelEvaluationException | SpelParseException e) { if (failOnUnresolvedExpression) { - String message = "Expression %s could not be resolved against context of type %s" - .formatted(expression, context.getClass()); + String message = "Expression %s could not be resolved against context of type %s".formatted( + expression, + context.getClass()); throw new OfficeStamperException(message, e); } else if (leaveEmptyOnExpressionError) { - log.warn( - "Expression {} could not be resolved against context root of type {}. Reason: {}. Set log" - + " level to TRACE to view Stacktrace.", - expression, - context.getClass(), - e.getMessage()); + log.warn("Expression {} could not be resolved against context root of type {}. Reason: {}. Set log" + + " level to TRACE to view Stacktrace.", expression, context.getClass(), e.getMessage()); log.trace("Reason for skipping expression:", e); paragraph.replace(expression, RunUtil.create("")); } else if (replaceUnresolvedExpressions) { - log.warn( - "Expression {} could not be resolved against context root of type {}. Reason: {}. Set log" - + " level to TRACE to view Stacktrace.", - expression, - context.getClass(), - e.getMessage()); + log.warn("Expression {} could not be resolved against context root of type {}. Reason: {}. Set log" + + " level to TRACE to view Stacktrace.", expression, context.getClass(), e.getMessage()); log.trace("Reason for skipping expression:", e); - paragraph.replace( - expression, - RunUtil.create(unresolvedExpressionsDefaultValue)); + paragraph.replace(expression, RunUtil.create(unresolvedExpressionsDefaultValue)); } else { // DO NOTHING } } } - if (lineBreakPlaceholder() != null) { + if (lineBreakPlaceholder != null) { replaceLineBreaks(paragraph); } } - - // TODO: Remove this intermediate method - private Placeholder lineBreakPlaceholder() { - return lineBreakPlaceholder; - } - private void replaceLineBreaks(Paragraph paragraph) { Br lineBreak = Context.getWmlObjectFactory() .createBr(); R run = RunUtil.create(lineBreak); while (paragraph.asString() - .contains(lineBreakPlaceholder().expression())) { - paragraph.replace(lineBreakPlaceholder(), run); + .contains(lineBreakPlaceholder.expression())) { + paragraph.replace(lineBreakPlaceholder, run); } } } From 0afd4f498534d166a38cf455973d5fecee9a5161 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Mon, 6 May 2024 13:50:41 +0800 Subject: [PATCH 110/134] Refactor error handling in PlaceholderReplacer The error handling within the PlaceholderReplacer class has been streamlined and enhanced. The try-catch block has been refactored for more specificity in handling SpelEvaluationException and SpelParseException. This will improve error messages for better traceability while debugging. --- .../docxstamper/core/PlaceholderReplacer.java | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java index d0879302..410d7b1a 100644 --- a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java +++ b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java @@ -107,28 +107,40 @@ public void resolveExpressions( var resolution = resolver.resolve(expression, context); var replacement = registry.resolve(document, expression, resolution); paragraph.replace(expression, replacement); - } catch (SpelEvaluationException | SpelParseException e) { + } catch (SpelEvaluationException e) { if (failOnUnresolvedExpression) { - String message = "Expression %s could not be resolved against context of type %s".formatted( - expression, - context.getClass()); + var template = "Expression %s could not be resolved against context of type %s"; + var message = template.formatted(expression, context.getClass()); throw new OfficeStamperException(message, e); } else if (leaveEmptyOnExpressionError) { - log.warn("Expression {} could not be resolved against context root of type {}. Reason: {}. Set log" - + " level to TRACE to view Stacktrace.", expression, context.getClass(), e.getMessage()); + var template = "Expression {} seems erroneous when evaluating against root of type {}." + + " Reason: {}." + + " Set log level to TRACE to view Stacktrace."; + log.warn(template, expression, context.getClass(), e.getMessage()); log.trace("Reason for skipping expression:", e); paragraph.replace(expression, RunUtil.create("")); } else if (replaceUnresolvedExpressions) { - log.warn("Expression {} could not be resolved against context root of type {}. Reason: {}. Set log" - + " level to TRACE to view Stacktrace.", expression, context.getClass(), e.getMessage()); + log.warn("Expression {} could not be resolved against context root of type {}." + + " Reason: {}. " + + "Set log level to TRACE to view Stacktrace.", + expression, + context.getClass(), + e.getMessage()); log.trace("Reason for skipping expression:", e); paragraph.replace(expression, RunUtil.create(unresolvedExpressionsDefaultValue)); } - else { - // DO NOTHING + } catch (SpelParseException e) { + if (leaveEmptyOnExpressionError) { + var template = "Expression {} seems erroneous when evaluating against root of type {}." + + " Reason: {}." + + " Set log level to TRACE to view Stacktrace."; + log.warn(template, expression, context.getClass(), e.getMessage()); + log.trace("Reason for skipping expression:", e); + paragraph.replace(expression, RunUtil.create("")); } + else throw new OfficeStamperException(e); } } if (lineBreakPlaceholder != null) { From 1901a3742c941e0e1faab8863bdb3010c1d4ac45 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Mon, 6 May 2024 13:58:41 +0800 Subject: [PATCH 111/134] Refactor PlaceholderReplacer and RunUtil classes The PlaceholderReplacer class has been refactored to simplify the replaceLineBreaks method. Unnecessary conditional checks have been removed and replaced with a more straightforward approach. Additionally, the RunUtil class formatting and method structure have been improved for better readability. New methods are also added to the Paragraph interface to enhance functionality. --- .../docxstamper/util/RunUtil.java | 43 +++++++++++-------- .../pro/verron/docxstamper/api/Paragraph.java | 12 ++++++ .../docxstamper/core/PlaceholderReplacer.java | 18 +++----- 3 files changed, 43 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java b/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java index 9694c78d..2e20e37e 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java +++ b/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java @@ -33,6 +33,7 @@ private RunUtil() { * Returns the text string of a run. * * @param run the run whose text to get. + * * @return {@link String} representation of the run. */ public static String getText(R run) { @@ -40,7 +41,8 @@ public static String getText(R run) { for (Object content : run.getContent()) { if (content instanceof JAXBElement) { result.append(getText((JAXBElement) content)); - } else if (content instanceof Text text) { + } + else if (content instanceof Text text) { result.append(getText(text)); } } @@ -68,12 +70,13 @@ private static CharSequence getText(Text text) { * Creates a new run with the given object as content. * * @param content the content of the run. + * * @return the newly created run. */ public static R create(Object content) { R run = factory.createR(); - run.getContent() - .add(content); + var runContent = run.getContent(); + runContent.add(content); return run; } @@ -82,6 +85,7 @@ public static R create(Object content) { * * @param text the initial text of the run. * @param parentParagraph the parent paragraph whose style to inherit. + * * @return the newly created run. */ public static R create(String text, P parentParagraph) { @@ -94,6 +98,7 @@ public static R create(String text, P parentParagraph) { * Creates a new run with the specified text. * * @param text the initial text of the run. + * * @return the newly created run. */ public static R create(String text) { @@ -110,10 +115,10 @@ public static R create(String text) { */ public static void applyParagraphStyle(P p, R run) { if (p.getPPr() != null && p.getPPr() - .getRPr() != null) { + .getRPr() != null) { RPr runProperties = new RPr(); StyleUtil.apply(p.getPPr() - .getRPr(), runProperties); + .getRPr(), runProperties); run.setRPr(runProperties); } } @@ -126,16 +131,17 @@ public static void applyParagraphStyle(P p, R run) { */ public static void setText(R run, String text) { run.getContent() - .clear(); + .clear(); Text textObj = createText(text); run.getContent() - .add(textObj); + .add(textObj); } /** * Creates a text object with the given text. * * @param text the text to set. + * * @return the newly created text object. */ public static Text createText(String text) { @@ -151,6 +157,7 @@ public static Text createText(String text) { * * @param maxWidth max width of the image * @param abstractImage the image + * * @return the run containing the image */ public static R createRunWithImage( @@ -178,9 +185,9 @@ public static R createRunWithImage( R run = factory.createR(); Drawing drawing = factory.createDrawing(); run.getContent() - .add(drawing); + .add(drawing); drawing.getAnchorOrInline() - .add(inline); + .add(inline); return run; @@ -197,16 +204,16 @@ private static Inline tryCreateImageInline( try { return maxWidth == null ? abstractImage.createImageInline(filenameHint, - altText, - id1, - id2, - false) + altText, + id1, + id2, + false) : abstractImage.createImageInline(filenameHint, - altText, - id1, - id2, - false, - maxWidth); + altText, + id1, + id2, + false, + maxWidth); } catch (Exception e) { throw new DocxStamperException(e); } diff --git a/src/main/java/pro/verron/docxstamper/api/Paragraph.java b/src/main/java/pro/verron/docxstamper/api/Paragraph.java index dee1f4df..eaf209a1 100644 --- a/src/main/java/pro/verron/docxstamper/api/Paragraph.java +++ b/src/main/java/pro/verron/docxstamper/api/Paragraph.java @@ -1,11 +1,23 @@ package pro.verron.docxstamper.api; +import org.docx4j.wml.R; + /** * The Paragraph interface represents a paragraph in a text document. * It provides methods for replacing a placeholder within the paragraph and retrieving the paragraph as a string. */ public interface Paragraph { + default void replaceAll(Placeholder placeholder, R replacement) { + while (contains(placeholder.expression())) { + replace(placeholder, replacement); + } + } + + default boolean contains(String expression) { + return asString().contains(expression); + } + /** * Replaces a placeholder in the given paragraph with the specified replacement. * diff --git a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java index 410d7b1a..0a17916c 100644 --- a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java +++ b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java @@ -2,9 +2,7 @@ import org.docx4j.jaxb.Context; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; -import org.docx4j.wml.Br; import org.docx4j.wml.P; -import org.docx4j.wml.R; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.expression.spel.SpelEvaluationException; @@ -143,18 +141,14 @@ else if (replaceUnresolvedExpressions) { else throw new OfficeStamperException(e); } } - if (lineBreakPlaceholder != null) { - replaceLineBreaks(paragraph); - } + replaceLineBreaks(paragraph); } private void replaceLineBreaks(Paragraph paragraph) { - Br lineBreak = Context.getWmlObjectFactory() - .createBr(); - R run = RunUtil.create(lineBreak); - while (paragraph.asString() - .contains(lineBreakPlaceholder.expression())) { - paragraph.replace(lineBreakPlaceholder, run); - } + var factory = Context.getWmlObjectFactory(); + var lineBreak = factory.createBr(); + var lineBreakRun = RunUtil.create(lineBreak); + paragraph.replaceAll(lineBreakPlaceholder, lineBreakRun); } + } From 519fc56b0afe9610eccb68a6b5e16ab8200920f5 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Wed, 8 May 2024 22:56:39 +0800 Subject: [PATCH 112/134] Refactor PowerpointVisitor and ExcelVisitor classes for better logging and error handling Updated the PowerpointVisitor and ExcelVisitor classes to include better error handling and logging features. Certain methods in these classes now have additional code to log ignored instances and handle unexpected visits. Also, refactored the structure of some methods to improve code readability and maintainability. --- .../verron/docxstamper/core/ExcelVisitor.java | 52 ++++++++++---- .../docxstamper/core/PowerpointVisitor.java | 70 +++++++++++++------ 2 files changed, 86 insertions(+), 36 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/core/ExcelVisitor.java b/src/main/java/pro/verron/docxstamper/core/ExcelVisitor.java index 86b5631c..81153ac6 100644 --- a/src/main/java/pro/verron/docxstamper/core/ExcelVisitor.java +++ b/src/main/java/pro/verron/docxstamper/core/ExcelVisitor.java @@ -11,16 +11,38 @@ import org.docx4j.openpackaging.parts.SpreadsheetML.WorkbookPart; import org.docx4j.openpackaging.parts.SpreadsheetML.WorksheetPart; import org.docx4j.openpackaging.parts.ThemePart; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.lang.Nullable; import org.xlsx4j.sml.*; import pro.verron.docxstamper.api.OfficeStamperException; +import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; abstract class ExcelVisitor { - public final void visit(Object object) { + private static final Logger logger = LoggerFactory.getLogger(ExcelVisitor.class); + + private static void unexpectedVisit(Object object) { + var env = System.getenv(); + var throwOnUnexpectedVisit = Boolean.parseBoolean(env.getOrDefault("throw-on-unexpected-visit", "false")); + var message = "Unknown case : %s %s".formatted(object, object.getClass()); + if (throwOnUnexpectedVisit) + throw new OfficeStamperException(message); + else + logger.debug(message); + } + + private static void ignore(@Nullable Object ignored1) { + logger.trace("ignored visit of '{}' object", ignored1); + } + + public final void visit(@Nullable Object object) { + before(object); try { if (object instanceof SpreadsheetMLPackage element) visit(element.getParts()); @@ -28,11 +50,11 @@ public final void visit(Object object) { else if (object instanceof Parts element) visit(element.getParts()); else if (object instanceof WorksheetPart element) visit(element.getContents()); else if (object instanceof WorkbookPart element) visit(element.getContents()); - else if (object instanceof DocPropsCorePart ignored) { /* do nothing */ } - else if (object instanceof DocPropsExtendedPart ignored) { /* do nothing */ } - else if (object instanceof Styles ignored) { /* do nothing */ } - else if (object instanceof SharedStrings element) {visit(element.getContents());} - else if (object instanceof ThemePart ignored) { /* do nothing */ } + else if (object instanceof DocPropsCorePart ignored) ignore(ignored); + else if (object instanceof DocPropsExtendedPart ignored) ignore(ignored); + else if (object instanceof Styles ignored) ignore(ignored); + else if (object instanceof SharedStrings element) visit(element.getContents()); + else if (object instanceof ThemePart ignored) ignore(ignored); else if (object instanceof Workbook element) visit(element.getSheets()); else if (object instanceof Sheets element) visit(element.getSheet()); @@ -43,22 +65,26 @@ else if (object instanceof ThemePart ignored) { /* do nothing */ } else if (object instanceof CTRst element) visit(element.getR()); else if (object instanceof CTSst element) visit(element.getSi()); else if (object instanceof CTRElt element) visit(element.getT()); - else if (object instanceof CTXstringWhitespace ignored) { /* do nothing */ } + else if (object instanceof CTXstringWhitespace ignored) ignore(ignored); else if (object instanceof JAXBElement element) visit(element.getValue()); else if (object instanceof Sheet element) visit(element.getState()); - else if (object instanceof STSheetState ignored) { /* do nothing */ } + else if (object instanceof STSheetState ignored) ignore(ignored); else if (object instanceof List element) element.forEach(this::visit); else if (object instanceof Set element) element.forEach(this::visit); else if (object instanceof Map element) visit(element.entrySet()); - else if (object instanceof Map.Entry element) visit(element.getValue()); - else if (object == null) { /* do nothing */ } - // TODO: replace this test throws by a noop before usage in the wild - else throw new OfficeStamperException("Unknown case : %s %s".formatted(object, object.getClass())); + else if (object instanceof Entry element) visit(element.getKey(), element.getValue()); + else if (object == null) ignore(null); + else unexpectedVisit(object); } catch (Docx4JException e) { throw new OfficeStamperException(e); } } - protected abstract void before(Object object); + private void visit(Object... objs) { + Arrays.stream(objs) + .forEach(this::visit); + } + + protected abstract void before(@Nullable Object object); } diff --git a/src/main/java/pro/verron/docxstamper/core/PowerpointVisitor.java b/src/main/java/pro/verron/docxstamper/core/PowerpointVisitor.java index 9c32d0d6..1dba3c21 100644 --- a/src/main/java/pro/verron/docxstamper/core/PowerpointVisitor.java +++ b/src/main/java/pro/verron/docxstamper/core/PowerpointVisitor.java @@ -9,32 +9,51 @@ import org.docx4j.openpackaging.parts.PresentationML.*; import org.docx4j.openpackaging.parts.WordprocessingML.ImageJpegPart; import org.pptx4j.pml.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.lang.Nullable; import pro.verron.docxstamper.api.OfficeStamperException; +import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Set; abstract class PowerpointVisitor { - public final void visit(Object object) { + private static final Logger logger = LoggerFactory.getLogger(PowerpointVisitor.class); + + private static void unexpectedVisit(Object object) { + var env = System.getenv(); + var throwOnUnexpectedVisit = Boolean.parseBoolean(env.getOrDefault("throw-on-unexpected-visit", "false")); + var message = "Unknown case : %s %s".formatted(object, object.getClass()); + if (throwOnUnexpectedVisit) + throw new OfficeStamperException(message); + else + logger.debug(message); + } + + private static void ignore(@Nullable Object ignored1) { + logger.trace("ignored visit of '{}' object", ignored1); + } + + public final void visit(@Nullable Object object) { before(object); try { if (object instanceof PresentationMLPackage element) visit(element.getParts()); - else if (object instanceof PartName ignored) { /* Do nothing */ } + else if (object instanceof PartName ignored) ignore(ignored); else if (object instanceof Parts element) visit(element.getParts()); - else if (object instanceof SlideLayoutPart ignored) { /* Do nothing */ } - else if (object instanceof ImageJpegPart ignored) { /* Do nothing */ } - else if (object instanceof ThemePart ignored) { /* Do nothing */ } - else if (object instanceof DocPropsCorePart ignored) { /* Do nothing */ } - else if (object instanceof DocPropsExtendedPart ignored) { /* Do nothing */ } - else if (object instanceof SlideMasterPart ignored) { /* Do nothing */ } - else if (object instanceof ViewPropertiesPart ignored) { /* Do nothing */ } - else if (object instanceof PresentationPropertiesPart ignored) { /* Do nothing */ } - else if (object instanceof TableStylesPart ignored) { /* Do nothing */ } + else if (object instanceof SlideLayoutPart ignored) ignore(ignored); + else if (object instanceof ImageJpegPart ignored) ignore(ignored); + else if (object instanceof ThemePart ignored) ignore(ignored); + else if (object instanceof DocPropsCorePart ignored) ignore(ignored); + else if (object instanceof DocPropsExtendedPart ignored) ignore(ignored); + else if (object instanceof SlideMasterPart ignored) ignore(ignored); + else if (object instanceof ViewPropertiesPart ignored) ignore(ignored); + else if (object instanceof PresentationPropertiesPart ignored) ignore(ignored); + else if (object instanceof TableStylesPart ignored) ignore(ignored); else if (object instanceof MainPresentationPart element) visit(element.getContents()); - else if (object instanceof List elements) elements.forEach(this::visit); - else if (object instanceof Map elements) elements.forEach(this::visit); else if (object instanceof SlidePart element) visit(element.getContents()); else if (object instanceof Sld element) visit(element.getCSld()); else if (object instanceof CommonSlideData element) visit(element.getSpTree()); @@ -42,22 +61,27 @@ else if (object instanceof TableStylesPart ignored) { /* Do nothing */ } else if (object instanceof Shape element) visit(element.getTxBody()); else if (object instanceof CTTextBody element) visit(element.getP()); else if (object instanceof CTTextParagraph element) visit(element.getEGTextRun()); - else if (object instanceof CTRegularTextRun ignored) { /* Do nothing */ } - else if (object instanceof Presentation.SldSz ignored) { /* Do Nothing */ } - else if (object instanceof Presentation ignored) { /* Do Nothing */ } - else if (object == null) { /* Do Nothing */ } - // TODO: replace this test throws by a noop before usage in the wild - else throw new OfficeStamperException("Unknown case %s : %s".formatted(object.getClass(), object)); + else if (object instanceof CTRegularTextRun ignored) ignore(ignored); + else if (object instanceof Presentation.SldSz ignored) ignore(ignored); + else if (object instanceof Presentation ignored) ignore(ignored); + + else if (object instanceof List element) element.forEach(this::visit); + else if (object instanceof Set element) element.forEach(this::visit); + else if (object instanceof Map element) visit(element.entrySet()); + else if (object instanceof Map.Entry element) visit(element.getKey(), element.getValue()); + + else if (object == null) ignore(null); + else unexpectedVisit(object); } catch (Docx4JException e) { throw new OfficeStamperException(e); } } - private void visit(Object o1, Object o2) { - visit(o1); - visit(o2); + private void visit(Object... objs) { + Arrays.stream(objs) + .forEach(this::visit); } - protected abstract void before(Object object); + protected abstract void before(@Nullable Object object); } From 156231d1f770651f073fead966a71e0eeecf421f Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Wed, 8 May 2024 22:57:09 +0800 Subject: [PATCH 113/134] Refactor CommentProcessorRegistry Modifications were made to the coding style in the CommentProcessorRegistry file. Key updates include altering the logger from non-static to static, reformatting method invocations for readability, and revising exception handling for better clarity and separation of concerns. --- .../processor/CommentProcessorRegistry.java | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java index 96a82eca..4ae0cf95 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java @@ -42,8 +42,9 @@ * @since 1.0.0 */ public class CommentProcessorRegistry { - private final Logger logger = LoggerFactory.getLogger( - CommentProcessorRegistry.class); + + private static final Logger logger = LoggerFactory.getLogger(CommentProcessorRegistry.class); + private final Map, Object> commentProcessors; private final boolean failOnUnresolvedExpression; private final ExpressionResolver expressionResolver; @@ -86,14 +87,14 @@ public void runProcessors( @Override protected void onRun(R run, P paragraph) { runProcessorsOnRunComment(document, comments, expressionContext, - paragraph, run) + paragraph, run) .ifPresent(proceedComments::add); } @Override protected void onParagraph(P paragraph) { runProcessorsOnParagraphComment(document, comments, - expressionContext, paragraph) + expressionContext, paragraph) .ifPresent(proceedComments::add); runProcessorsOnInlineContent(expressionContext, paragraph); } @@ -118,7 +119,7 @@ private Optional runProcessorsOnRunComment( var comment = CommentUtil.getCommentAround(run, document); return comment.flatMap( c -> runCommentProcessors(comments, expressionContext, c, - paragraph, run, document)); + paragraph, run, document)); } /** @@ -141,9 +142,9 @@ private Optional runProcessorsOnParagraphComment( return CommentUtil .getCommentFor(paragraph, document) .flatMap(c -> this.runCommentProcessors(comments, - expressionContext, c, - paragraph, null, - document)); + expressionContext, c, + paragraph, null, + document)); } /** @@ -178,7 +179,8 @@ private void runProcessorsOnInlineContent( expression); if (failOnUnresolvedExpression) { throw new DocxStamperException(msg, e); - } else { + } + else { logger.warn(msg, e); } } @@ -220,10 +222,12 @@ private Optional runCommentProcessors( } catch (SpelEvaluationException | SpelParseException e) { if (failOnUnresolvedExpression) { throw new UnresolvedExpressionException(commentExpression.toString(), - e); - } else { + e); + } + else { logger.warn(String.format( - "Skipping comment expression '%s' because it can not be resolved by any comment processor. Reason: %s. Set log level to TRACE to view Stacktrace.", + "Skipping comment expression '%s' because it can not be resolved by any comment processor. " + + "Reason: %s. Set log level to TRACE to view Stacktrace.", commentExpression, e.getMessage())); logger.trace("Reason for skipping comment: ", e); From b54192333bbe3c2fc5fee2c177d0b90e86c70c02 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 9 May 2024 08:30:58 +0800 Subject: [PATCH 114/134] Move Excel and Powerpoint Support to Experimental Removed the standard support for Excel and Powerpoint and moved these into an experimental package. This included moving Powerpoint and Excel related files to a dedicated 'experimental' package. Also, 'BasicPowerpointTest' code adjustments to reflect the package change. --- src/main/java/module-info.java | 2 + .../docxstamper/util/RunUtil.java | 16 +- .../preset/ExperimentalStampers.java | 34 +++ .../preset/OfficeStamperConfigurations.java | 9 - .../docxstamper/preset/OfficeStampers.java | 26 --- .../PowerpointStamperConfiguration.java | 201 ------------------ .../experimental}/ExcelCollector.java | 2 +- .../experimental}/ExcelParagraph.java | 2 +- .../experimental}/ExcelStamper.java | 2 +- .../experimental}/ExcelVisitor.java | 2 +- .../experimental}/PowerpointCollector.java | 2 +- .../experimental}/PowerpointParagraph.java | 2 +- .../experimental}/PowerpointRun.java | 2 +- .../experimental}/PowerpointStamper.java | 3 +- .../experimental}/PowerpointVisitor.java | 2 +- .../docxstamper/test/BasicExcelTest.java | 2 +- .../docxstamper/test/BasicPowerpointTest.java | 15 +- .../verron/docxstamper/test/Stringifier.java | 29 ++- 18 files changed, 74 insertions(+), 279 deletions(-) create mode 100644 src/main/java/pro/verron/docxstamper/preset/ExperimentalStampers.java delete mode 100644 src/main/java/pro/verron/docxstamper/preset/PowerpointStamperConfiguration.java rename src/main/java/pro/verron/{docxstamper/core => officestamper/experimental}/ExcelCollector.java (93%) rename src/main/java/pro/verron/{docxstamper/core => officestamper/experimental}/ExcelParagraph.java (97%) rename src/main/java/pro/verron/{docxstamper/core => officestamper/experimental}/ExcelStamper.java (97%) rename src/main/java/pro/verron/{docxstamper/core => officestamper/experimental}/ExcelVisitor.java (98%) rename src/main/java/pro/verron/{docxstamper/core => officestamper/experimental}/PowerpointCollector.java (97%) rename src/main/java/pro/verron/{docxstamper/core => officestamper/experimental}/PowerpointParagraph.java (99%) rename src/main/java/pro/verron/{docxstamper/core => officestamper/experimental}/PowerpointRun.java (97%) rename src/main/java/pro/verron/{docxstamper/core => officestamper/experimental}/PowerpointStamper.java (95%) rename src/main/java/pro/verron/{docxstamper/core => officestamper/experimental}/PowerpointVisitor.java (98%) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index c03ecf3c..3aaf4056 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -63,4 +63,6 @@ exports org.wickedsource.docxstamper.util; exports org.wickedsource.docxstamper.processor; exports org.wickedsource.docxstamper.processor.table; + exports pro.verron.officestamper.experimental to pro.verron.officestamper.test; + opens pro.verron.officestamper.experimental to pro.verron.officestamper.test; } diff --git a/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java b/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java index 2e20e37e..aa7426c1 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java +++ b/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java @@ -7,6 +7,7 @@ import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage; import org.docx4j.wml.*; import org.wickedsource.docxstamper.api.DocxStamperException; +import pro.verron.docxstamper.api.OfficeStamperException; import java.util.Objects; import java.util.Random; @@ -203,19 +204,10 @@ private static Inline tryCreateImageInline( ) { try { return maxWidth == null - ? abstractImage.createImageInline(filenameHint, - altText, - id1, - id2, - false) - : abstractImage.createImageInline(filenameHint, - altText, - id1, - id2, - false, - maxWidth); + ? abstractImage.createImageInline(filenameHint, altText, id1, id2, false) + : abstractImage.createImageInline(filenameHint, altText, id1, id2, false, maxWidth); } catch (Exception e) { - throw new DocxStamperException(e); + throw new OfficeStamperException(e); } } } diff --git a/src/main/java/pro/verron/docxstamper/preset/ExperimentalStampers.java b/src/main/java/pro/verron/docxstamper/preset/ExperimentalStampers.java new file mode 100644 index 00000000..c63ef3d3 --- /dev/null +++ b/src/main/java/pro/verron/docxstamper/preset/ExperimentalStampers.java @@ -0,0 +1,34 @@ +package pro.verron.docxstamper.preset; + +import org.docx4j.openpackaging.packages.PresentationMLPackage; +import org.docx4j.openpackaging.packages.SpreadsheetMLPackage; +import pro.verron.docxstamper.api.OfficeStamper; +import pro.verron.officestamper.experimental.ExcelStamper; +import pro.verron.officestamper.experimental.PowerpointStamper; + +public class ExperimentalStampers { + /** + * Returns a new instance of the OfficeStamper implementation + * for stamping Powerpoint presentations with context and writing + * the result to an OutputStream. + * + * @return a new OfficeStamper instance for Powerpoint presentations + * + * @since 1.6.8 + */ + public static OfficeStamper pptxStamper() { + return new PowerpointStamper(); + } + + /** + * Returns a new instance of the OfficeStamper implementation + * for stamping Excel templates with context and writing the result to an OutputStream. + * + * @return a new OfficeStamper instance for Excel templates + * + * @since 1.6.8 + */ + public static OfficeStamper xlsxStamper() { + return new ExcelStamper(); + } +} diff --git a/src/main/java/pro/verron/docxstamper/preset/OfficeStamperConfigurations.java b/src/main/java/pro/verron/docxstamper/preset/OfficeStamperConfigurations.java index f3033429..363bd487 100644 --- a/src/main/java/pro/verron/docxstamper/preset/OfficeStamperConfigurations.java +++ b/src/main/java/pro/verron/docxstamper/preset/OfficeStamperConfigurations.java @@ -34,13 +34,4 @@ public static OfficeStamperConfiguration standard() { return new DocxStamperConfiguration(); } - /** - * Returns a new instance of OfficeStamperConfiguration specifically for PowerPoint files. - * - * @return a new OfficeStamperConfiguration instance for PowerPoint files - */ - public static OfficeStamperConfiguration powerpoint() { - return new PowerpointStamperConfiguration(); - } - } diff --git a/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java b/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java index 597a9129..24540692 100644 --- a/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java +++ b/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java @@ -1,15 +1,11 @@ package pro.verron.docxstamper.preset; -import org.docx4j.openpackaging.packages.PresentationMLPackage; -import org.docx4j.openpackaging.packages.SpreadsheetMLPackage; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.wickedsource.docxstamper.DocxStamper; import org.wickedsource.docxstamper.preprocessor.MergeSameStyleRuns; import org.wickedsource.docxstamper.preprocessor.RemoveProofErrors; import pro.verron.docxstamper.api.OfficeStamper; import pro.verron.docxstamper.api.OfficeStamperConfiguration; -import pro.verron.docxstamper.core.ExcelStamper; -import pro.verron.docxstamper.core.PowerpointStamper; /** * Main class of the docx-stamper library. @@ -46,26 +42,4 @@ public static OfficeStamper docxStamper() { return new DocxStamper<>(OfficeStamperConfigurations.standardWithPreprocessing()); } - /** - * Returns a new instance of the OfficeStamper implementation - * for stamping Powerpoint presentations with context and writing - * the result to an OutputStream. - * - * @return a new OfficeStamper instance for Powerpoint presentations - * @since 1.6.8 - */ - public static OfficeStamper pptxStamper() { - return new PowerpointStamper(); - } - - /** - * Returns a new instance of the OfficeStamper implementation - * for stamping Excel templates with context and writing the result to an OutputStream. - * - * @return a new OfficeStamper instance for Excel templates - * @since 1.6.8 - */ - public static OfficeStamper xlsxStamper() { - return new ExcelStamper(); - } } diff --git a/src/main/java/pro/verron/docxstamper/preset/PowerpointStamperConfiguration.java b/src/main/java/pro/verron/docxstamper/preset/PowerpointStamperConfiguration.java deleted file mode 100644 index 5fd30fa3..00000000 --- a/src/main/java/pro/verron/docxstamper/preset/PowerpointStamperConfiguration.java +++ /dev/null @@ -1,201 +0,0 @@ -package pro.verron.docxstamper.preset; - -import org.docx4j.openpackaging.packages.WordprocessingMLPackage; -import org.springframework.expression.spel.SpelParserConfiguration; -import pro.verron.docxstamper.api.*; - -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; - -final class PowerpointStamperConfiguration - implements OfficeStamperConfiguration { - @Override - public Optional nullReplacementValue() { - return Optional.empty(); - } - - @Override - public boolean isFailOnUnresolvedExpression() { - return false; - } - - @Override - public OfficeStamperConfiguration setFailOnUnresolvedExpression( - boolean failOnUnresolvedExpression - ) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public OfficeStamperConfiguration nullValuesDefault(String nullValuesDefault) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public OfficeStamperConfiguration replaceNullValues(boolean replaceNullValues) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public OfficeStamperConfiguration unresolvedExpressionsDefaultValue( - String unresolvedExpressionsDefaultValue - ) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public OfficeStamperConfiguration replaceUnresolvedExpressions( - boolean replaceUnresolvedExpressions - ) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public OfficeStamperConfiguration leaveEmptyOnExpressionError( - boolean leaveEmpty - ) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public OfficeStamperConfiguration addTypeResolver( - Class resolvedType, - ITypeResolver resolver - ) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public OfficeStamperConfiguration exposeInterfaceToExpressionLanguage( - Class interfaceClass, - Object implementation - ) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public OfficeStamperConfiguration addCommentProcessor( - Class interfaceClass, - Function commentProcessorFactory - ) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public OfficeStamper build() { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public void addPreprocessor(PreProcessor preprocessor) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public boolean isReplaceUnresolvedExpressions() { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public boolean isLeaveEmptyOnExpressionError() { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public String getUnresolvedExpressionsDefaultValue() { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public String getLineBreakPlaceholder() { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public OfficeStamperConfiguration setLineBreakPlaceholder(String lineBreakPlaceholder) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public EvaluationContextConfigurer getEvaluationContextConfigurer() { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public OfficeStamperConfiguration setEvaluationContextConfigurer( - EvaluationContextConfigurer evaluationContextConfigurer - ) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public SpelParserConfiguration getSpelParserConfiguration() { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public OfficeStamperConfiguration setSpelParserConfiguration( - SpelParserConfiguration spelParserConfiguration - ) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public Map, Object> getExpressionFunctions() { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public Map, ITypeResolver> getTypeResolvers() { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public ITypeResolver getDefaultTypeResolver() { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public OfficeStamperConfiguration setDefaultTypeResolver( - ITypeResolver defaultResolver - ) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public Map, Function> getCommentProcessors() { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public boolean isReplaceNullValues() { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public String getNullValuesDefault() { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public List getPreprocessors() { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public List getResolvers() { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public OfficeStamperConfiguration setResolvers(List resolvers) { - throw new UnsupportedOperationException("Not supported."); - } - - @Override - public OfficeStamperConfiguration addResolver(ObjectResolver resolver) { - throw new UnsupportedOperationException("Not supported."); - } -} diff --git a/src/main/java/pro/verron/docxstamper/core/ExcelCollector.java b/src/main/java/pro/verron/officestamper/experimental/ExcelCollector.java similarity index 93% rename from src/main/java/pro/verron/docxstamper/core/ExcelCollector.java rename to src/main/java/pro/verron/officestamper/experimental/ExcelCollector.java index fa96d5ca..7db64139 100644 --- a/src/main/java/pro/verron/docxstamper/core/ExcelCollector.java +++ b/src/main/java/pro/verron/officestamper/experimental/ExcelCollector.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.core; +package pro.verron.officestamper.experimental; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/pro/verron/docxstamper/core/ExcelParagraph.java b/src/main/java/pro/verron/officestamper/experimental/ExcelParagraph.java similarity index 97% rename from src/main/java/pro/verron/docxstamper/core/ExcelParagraph.java rename to src/main/java/pro/verron/officestamper/experimental/ExcelParagraph.java index 264975c4..edeb68fe 100644 --- a/src/main/java/pro/verron/docxstamper/core/ExcelParagraph.java +++ b/src/main/java/pro/verron/officestamper/experimental/ExcelParagraph.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.core; +package pro.verron.officestamper.experimental; import org.xlsx4j.sml.CTRst; import pro.verron.docxstamper.api.Placeholder; diff --git a/src/main/java/pro/verron/docxstamper/core/ExcelStamper.java b/src/main/java/pro/verron/officestamper/experimental/ExcelStamper.java similarity index 97% rename from src/main/java/pro/verron/docxstamper/core/ExcelStamper.java rename to src/main/java/pro/verron/officestamper/experimental/ExcelStamper.java index 54fa1721..cec1a204 100644 --- a/src/main/java/pro/verron/docxstamper/core/ExcelStamper.java +++ b/src/main/java/pro/verron/officestamper/experimental/ExcelStamper.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.core; +package pro.verron.officestamper.experimental; import org.docx4j.openpackaging.exceptions.Docx4JException; import org.docx4j.openpackaging.packages.SpreadsheetMLPackage; diff --git a/src/main/java/pro/verron/docxstamper/core/ExcelVisitor.java b/src/main/java/pro/verron/officestamper/experimental/ExcelVisitor.java similarity index 98% rename from src/main/java/pro/verron/docxstamper/core/ExcelVisitor.java rename to src/main/java/pro/verron/officestamper/experimental/ExcelVisitor.java index 81153ac6..dbf5441b 100644 --- a/src/main/java/pro/verron/docxstamper/core/ExcelVisitor.java +++ b/src/main/java/pro/verron/officestamper/experimental/ExcelVisitor.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.core; +package pro.verron.officestamper.experimental; import jakarta.xml.bind.JAXBElement; import org.docx4j.openpackaging.exceptions.Docx4JException; diff --git a/src/main/java/pro/verron/docxstamper/core/PowerpointCollector.java b/src/main/java/pro/verron/officestamper/experimental/PowerpointCollector.java similarity index 97% rename from src/main/java/pro/verron/docxstamper/core/PowerpointCollector.java rename to src/main/java/pro/verron/officestamper/experimental/PowerpointCollector.java index add76da7..f5f53698 100644 --- a/src/main/java/pro/verron/docxstamper/core/PowerpointCollector.java +++ b/src/main/java/pro/verron/officestamper/experimental/PowerpointCollector.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.core; +package pro.verron.officestamper.experimental; import java.util.ArrayList; diff --git a/src/main/java/pro/verron/docxstamper/core/PowerpointParagraph.java b/src/main/java/pro/verron/officestamper/experimental/PowerpointParagraph.java similarity index 99% rename from src/main/java/pro/verron/docxstamper/core/PowerpointParagraph.java rename to src/main/java/pro/verron/officestamper/experimental/PowerpointParagraph.java index 70e39983..955b887d 100644 --- a/src/main/java/pro/verron/docxstamper/core/PowerpointParagraph.java +++ b/src/main/java/pro/verron/officestamper/experimental/PowerpointParagraph.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.core; +package pro.verron.officestamper.experimental; import org.docx4j.dml.CTRegularTextRun; import org.docx4j.dml.CTTextCharacterProperties; diff --git a/src/main/java/pro/verron/docxstamper/core/PowerpointRun.java b/src/main/java/pro/verron/officestamper/experimental/PowerpointRun.java similarity index 97% rename from src/main/java/pro/verron/docxstamper/core/PowerpointRun.java rename to src/main/java/pro/verron/officestamper/experimental/PowerpointRun.java index 153cbe83..00de250b 100644 --- a/src/main/java/pro/verron/docxstamper/core/PowerpointRun.java +++ b/src/main/java/pro/verron/officestamper/experimental/PowerpointRun.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.core; +package pro.verron.officestamper.experimental; import org.docx4j.dml.CTRegularTextRun; diff --git a/src/main/java/pro/verron/docxstamper/core/PowerpointStamper.java b/src/main/java/pro/verron/officestamper/experimental/PowerpointStamper.java similarity index 95% rename from src/main/java/pro/verron/docxstamper/core/PowerpointStamper.java rename to src/main/java/pro/verron/officestamper/experimental/PowerpointStamper.java index 4587055f..32a5a55c 100644 --- a/src/main/java/pro/verron/docxstamper/core/PowerpointStamper.java +++ b/src/main/java/pro/verron/officestamper/experimental/PowerpointStamper.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.core; +package pro.verron.officestamper.experimental; import org.docx4j.dml.CTRegularTextRun; import org.docx4j.dml.CTTextParagraph; @@ -9,6 +9,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; import pro.verron.docxstamper.api.OfficeStamper; import pro.verron.docxstamper.api.OfficeStamperException; +import pro.verron.docxstamper.core.Placeholders; import java.io.OutputStream; import java.util.List; diff --git a/src/main/java/pro/verron/docxstamper/core/PowerpointVisitor.java b/src/main/java/pro/verron/officestamper/experimental/PowerpointVisitor.java similarity index 98% rename from src/main/java/pro/verron/docxstamper/core/PowerpointVisitor.java rename to src/main/java/pro/verron/officestamper/experimental/PowerpointVisitor.java index 1dba3c21..efbd5f6d 100644 --- a/src/main/java/pro/verron/docxstamper/core/PowerpointVisitor.java +++ b/src/main/java/pro/verron/officestamper/experimental/PowerpointVisitor.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.core; +package pro.verron.officestamper.experimental; import org.docx4j.dml.CTRegularTextRun; import org.docx4j.dml.CTTextBody; diff --git a/src/test/java/pro/verron/docxstamper/test/BasicExcelTest.java b/src/test/java/pro/verron/docxstamper/test/BasicExcelTest.java index f69b5c28..41b3598f 100644 --- a/src/test/java/pro/verron/docxstamper/test/BasicExcelTest.java +++ b/src/test/java/pro/verron/docxstamper/test/BasicExcelTest.java @@ -9,7 +9,7 @@ import static org.docx4j.openpackaging.packages.SpreadsheetMLPackage.load; import static org.junit.jupiter.api.Assertions.assertEquals; -import static pro.verron.docxstamper.preset.OfficeStampers.xlsxStamper; +import static pro.verron.docxstamper.preset.ExperimentalStampers.xlsxStamper; import static pro.verron.docxstamper.test.IOStreams.getInputStream; import static pro.verron.docxstamper.test.IOStreams.getOutputStream; import static pro.verron.docxstamper.test.Stringifier.stringifyExcel; diff --git a/src/test/java/pro/verron/docxstamper/test/BasicPowerpointTest.java b/src/test/java/pro/verron/docxstamper/test/BasicPowerpointTest.java index 8061a28e..5c406a28 100644 --- a/src/test/java/pro/verron/docxstamper/test/BasicPowerpointTest.java +++ b/src/test/java/pro/verron/docxstamper/test/BasicPowerpointTest.java @@ -4,27 +4,30 @@ import org.docx4j.openpackaging.packages.PresentationMLPackage; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.preset.OfficeStampers; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.nio.file.Files; import java.nio.file.Path; +import static java.nio.file.Files.newInputStream; +import static pro.verron.docxstamper.preset.ExperimentalStampers.pptxStamper; +import static pro.verron.docxstamper.test.IOStreams.getInputStream; +import static pro.verron.docxstamper.test.IOStreams.getOutputStream; + public class BasicPowerpointTest { @Test public void testStamper() throws IOException, Docx4JException { - var stamper = OfficeStampers.pptxStamper(); + var stamper = pptxStamper(); var templatePath = Path.of("test", "sources", "powerpoint-base.pptx"); - var templateStream = Files.newInputStream(templatePath); + var templateStream = newInputStream(templatePath); record Person(String name) {} var context = new Person("Bart"); PresentationMLPackage load = PresentationMLPackage.load(templateStream); - OutputStream outputStream = IOStreams.getOutputStream(); + OutputStream outputStream = getOutputStream(); stamper.stamp(load, context, outputStream); - InputStream inputStream = IOStreams.getInputStream(outputStream); + InputStream inputStream = getInputStream(outputStream); PresentationMLPackage presentationMLPackage = PresentationMLPackage.load(inputStream); Assertions.assertEquals(""" Hello diff --git a/src/test/java/pro/verron/docxstamper/test/Stringifier.java b/src/test/java/pro/verron/docxstamper/test/Stringifier.java index cb718b1c..d58938a9 100644 --- a/src/test/java/pro/verron/docxstamper/test/Stringifier.java +++ b/src/test/java/pro/verron/docxstamper/test/Stringifier.java @@ -18,9 +18,9 @@ import org.xlsx4j.org.apache.poi.ss.usermodel.DataFormatter; import org.xlsx4j.sml.Cell; import pro.verron.docxstamper.api.OfficeStamperException; -import pro.verron.docxstamper.core.ExcelCollector; -import pro.verron.docxstamper.core.PowerpointCollector; -import pro.verron.docxstamper.core.PowerpointParagraph; +import pro.verron.officestamper.experimental.ExcelCollector; +import pro.verron.officestamper.experimental.PowerpointCollector; +import pro.verron.officestamper.experimental.PowerpointParagraph; import java.math.BigInteger; import java.security.MessageDigest; @@ -45,18 +45,6 @@ */ public class Stringifier { - public static String stringifyPowerpoint(PresentationMLPackage presentation) { - var collector = new PowerpointCollector<>(CTTextParagraph.class); - collector.visit(presentation); - var collected = collector.collect(); - - var powerpoint = new StringBuilder(); - for (CTTextParagraph paragraph : collected) { - powerpoint.append(new PowerpointParagraph(paragraph).asString()); - } - return powerpoint.toString(); - } - private final Supplier documentSupplier; /** @@ -68,6 +56,17 @@ public Stringifier(Supplier documentSupplier) { this.documentSupplier = documentSupplier; } + public static String stringifyPowerpoint(PresentationMLPackage presentation) { + var collector = new PowerpointCollector<>(CTTextParagraph.class); + collector.visit(presentation); + var collected = collector.collect(); + + var powerpoint = new StringBuilder(); + for (CTTextParagraph paragraph : collected) { + powerpoint.append(new PowerpointParagraph(paragraph).asString()); + } + return powerpoint.toString(); + } public static String stringifyExcel(SpreadsheetMLPackage presentation) { var collector = new ExcelCollector<>(Cell.class); From 3e5a55f74120f87023420938cfb2879c3d21500e Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Thu, 9 May 2024 09:08:42 +0800 Subject: [PATCH 115/134] Refactored PlaceholderReplacer and updated tests The PlaceholderReplacer class has been refactored to improve the handling of line breaks. Two new methods, getBr() and getR(), have been added to encapsulate creation of line break and run elements. Tests have been significantly restructured for clarity and simplicity. --- .../docxstamper/util/RunUtil.java | 14 - .../docxstamper/core/PlaceholderReplacer.java | 20 +- .../verron/docxstamper/test/DefaultTests.java | 1091 ++++++++--------- .../verron/docxstamper/test/Stringifier.java | 6 +- test/sources/CustomExpressionFunction.docx | Bin 5710 -> 17227 bytes 5 files changed, 566 insertions(+), 565 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java b/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java index aa7426c1..37065d09 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java +++ b/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java @@ -67,20 +67,6 @@ private static CharSequence getText(Text text) { : value.trim(); // trimming value if spaces are not to be preserved (simulates behavior of Word;) } - /** - * Creates a new run with the given object as content. - * - * @param content the content of the run. - * - * @return the newly created run. - */ - public static R create(Object content) { - R run = factory.createR(); - var runContent = run.getContent(); - runContent.add(content); - return run; - } - /** * Creates a new run with the specified text and inherits the style of the parent paragraph. * diff --git a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java index 0a17916c..ed5bc4f3 100644 --- a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java +++ b/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java @@ -2,7 +2,9 @@ import org.docx4j.jaxb.Context; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; +import org.docx4j.wml.Br; import org.docx4j.wml.P; +import org.docx4j.wml.R; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.expression.spel.SpelEvaluationException; @@ -145,10 +147,24 @@ else if (replaceUnresolvedExpressions) { } private void replaceLineBreaks(Paragraph paragraph) { + var lineBreak = getBr(); + var run = getR(lineBreak); + paragraph.replaceAll(lineBreakPlaceholder, run); + } + + private static Br getBr() { var factory = Context.getWmlObjectFactory(); var lineBreak = factory.createBr(); - var lineBreakRun = RunUtil.create(lineBreak); - paragraph.replaceAll(lineBreakPlaceholder, lineBreakRun); + lineBreak.setType(null); + return lineBreak; + } + + private static R getR(Br lineBreak) { + var factory = Context.getWmlObjectFactory(); + var run = factory.createR(); + run.getContent() + .add(lineBreak); + return run; } } diff --git a/src/test/java/pro/verron/docxstamper/test/DefaultTests.java b/src/test/java/pro/verron/docxstamper/test/DefaultTests.java index c62b6a39..8dd2c74e 100644 --- a/src/test/java/pro/verron/docxstamper/test/DefaultTests.java +++ b/src/test/java/pro/verron/docxstamper/test/DefaultTests.java @@ -37,148 +37,173 @@ public class DefaultTests { /** - *

getResource.

+ *

tests.

* - * @param path a {@link java.nio.file.Path} object - * @return a {@link java.io.InputStream} object + * @return a {@link java.util.stream.Stream} object */ - public static InputStream getResource(Path path) { - try { - var testRoot = Path.of("test", "sources"); - var resolve = testRoot.resolve(path); - return Files.newInputStream(resolve); - } catch (IOException e) { - throw new RuntimeException(e); - } + public static Stream tests() { + return Stream.of(tabulations(), + whitespaces(), + ternary(), + repeatingRows(), + replaceWordWithIntegrationTest(), + replaceNullExpressionTest(), + repeatTableRowKeepsFormatTest(), + repeatParagraphTest(), + repeatDocPartWithImageTestShouldImportImageDataInTheMainDocument(), + repeatDocPartWithImagesInSourceTestshouldReplicateImageFromTheMainDocumentInTheSubTemplate(), + repeatDocPartTest(), + repeatDocPartNestingTest(), + repeatDocPartAndCommentProcessorsIsolationTest_repeatDocPartShouldNotUseSameCommentProcessorInstancesForSubtemplate(), + changingPageLayoutTest_shouldKeepSectionBreakOrientationInRepeatParagraphWithoutSectionBreakInsideComment(), + changingPageLayoutTest_shouldKeepSectionBreakOrientationInRepeatParagraphWithSectionBreakInsideComment(), + changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithSectionBreaksInsideComment(), + replaceNullExpressionTest2(), + changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithSectionBreaksInsideCommentAndTableAsLastElement(), + changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithoutSectionBreaksInsideComment(), + conditionalDisplayOfParagraphsTest_processorExpressionsInCommentsAreResolved(), + conditionalDisplayOfParagraphsTest_inlineProcessorExpressionsAreResolved(), + conditionalDisplayOfParagraphsTest_unresolvedInlineProcessorExpressionsAreRemoved(), + conditionalDisplayOfTableRowsTest(), + conditionalDisplayOfTableBug32Test(), + conditionalDisplayOfTableTest(), + customEvaluationContextConfigurerTest_customEvaluationContextConfigurerIsHonored(), + customExpressionFunctionTest(), + customTypeResolverTest(), + dateReplacementTest(), + expressionReplacementInGlobalParagraphsTest(), + expressionReplacementInTablesTest(), + expressionReplacementWithFormattingTest(), + expressionWithSurroundingSpacesTest(), + expressionReplacementWithCommentTest(), + imageReplacementInGlobalParagraphsTest(), + imageReplacementInGlobalParagraphsTestWithMaxWidth(), + leaveEmptyOnExpressionErrorTest(), + lineBreakReplacementTest(), + mapAccessorAndReflectivePropertyAccessorTest_shouldResolveMapAndPropertyPlaceholders(), + nullPointerResolutionTest_testWithDefaultSpel(), + nullPointerResolutionTest_testWithCustomSpel(), + customCommentProcessor()); } - private static Arguments replaceWordWithIntegrationTest() { - return of("replaceWordWithIntegrationTest", - OfficeStamperConfigurations.standard(), - name("Simpsons"), - getResource(Path.of("integration", - "ReplaceWordWithIntegrationTest.docx")), - """ - ReplaceWordWith Integration - ❬This variable ❬name❘b=true❭❬ ❘b=true❭should be resolved to the value Simpsons.❘b=true❭ - This variable ❬name❘b=true❭ should be resolved to the value Simpsons. - """); + private static Arguments tabulations() { + return of("Tabulation should be preserved", + OfficeStamperConfigurations.standard(), + name("Homer Simpson"), + getResource(Path.of("TabsIndentationTest.docx")), + """ + ❬❬Tab❘lang=en-US❭❬|TAB|❘lang=en-US❭❬Homer Simpson❘lang=en-US❭❘lang=en-US❭ + ❬❬Space❘lang=en-US❭❬ ❘lang=en-US❭❬Homer Simpson❘lang=en-US❭❘lang=en-US❭"""); + } + + private static Arguments whitespaces() { + return of("White spaces should be preserved", + OfficeStamperConfigurations.standard(), + name("Homer Simpson"), + getResource(Path.of("TabsIndentationTest.docx")), + """ + ❬❬Tab❘lang=en-US❭❬|TAB|❘lang=en-US❭❬Homer Simpson❘lang=en-US❭❘lang=en-US❭ + ❬❬Space❘lang=en-US❭❬ ❘lang=en-US❭❬Homer Simpson❘lang=en-US❭❘lang=en-US❭"""); + } + + private static Arguments ternary() { + return of("Ternary operators should function", + OfficeStamperConfigurations.standard(), + name("Homer"), + getResource(Path.of("TernaryOperatorTest.docx")), + """ + Expression Replacement with ternary operator + This paragraph is untouched. + Some replacement before the ternary operator: Homer. + Homer <-- this should read "Homer". + <-- this should be empty."""); } private static Arguments repeatingRows() { return of("Repeating table rows should be possible", - OfficeStamperConfigurations.standard(), - roles(role("Homer Simpson", "Dan Castellaneta"), + OfficeStamperConfigurations.standard(), + roles(role("Homer Simpson", "Dan Castellaneta"), role("Marge Simpson", "Julie Kavner"), role("Bart Simpson", "Nancy Cartwright"), role("Kent Brockman", "Harry Shearer"), role("Disco Stu", "Hank Azaria"), role("Krusty the Clown", "Dan Castellaneta")), - getResource(Path.of("RepeatTableRowTest.docx")), - """ - ❬Repeating Table Rows❘spacing={after=120,before=240}❭ - ❬❬List of Simpsons characters❘b=true❭❘b=true,spacing={after=120,before=240}❭ - ❬❬Character name❘b=true❭❘b=true❭ - ❬❬Voice ❘b=true❭❬Actor❘b=true❭❘b=true❭ - Homer Simpson - Dan Castellaneta - Marge Simpson - Julie Kavner - Bart Simpson - Nancy Cartwright - Kent Brockman - Harry Shearer - Disco Stu - Hank Azaria - Krusty the Clown - Dan Castellaneta - - ❬There are ❬6❘lang=de-DE❭ characters in the above table.❘lang=de-DE,spacing={after=140,before=0}❭"""); - } - - private static Arguments ternary() { - return of("Ternary operators should function", - OfficeStamperConfigurations.standard(), - name("Homer"), - getResource(Path.of("TernaryOperatorTest.docx")), - """ - Expression Replacement with ternary operator - This paragraph is untouched. - Some replacement before the ternary operator: Homer. - Homer <-- this should read "Homer". - <-- this should be empty."""); - } - - private static Arguments whitespaces() { - return of("White spaces should be preserved", - OfficeStamperConfigurations.standard(), - name("Homer Simpson"), - getResource(Path.of("TabsIndentationTest.docx")), - """ - ❬❬Tab❘lang=en-US❭❬|TAB|❘lang=en-US❭❬Homer Simpson❘lang=en-US❭❘lang=en-US❭ - ❬❬Space❘lang=en-US❭❬ ❘lang=en-US❭❬Homer Simpson❘lang=en-US❭❘lang=en-US❭"""); + getResource(Path.of("RepeatTableRowTest.docx")), + """ + ❬Repeating Table Rows❘spacing={after=120,before=240}❭ + ❬❬List of Simpsons characters❘b=true❭❘b=true,spacing={after=120,before=240}❭ + ❬❬Character name❘b=true❭❘b=true❭ + ❬❬Voice ❘b=true❭❬Actor❘b=true❭❘b=true❭ + Homer Simpson + Dan Castellaneta + Marge Simpson + Julie Kavner + Bart Simpson + Nancy Cartwright + Kent Brockman + Harry Shearer + Disco Stu + Hank Azaria + Krusty the Clown + Dan Castellaneta + + ❬There are ❬6❘lang=de-DE❭ characters in the above table.❘lang=de-DE,spacing={after=140,before=0}❭"""); } - private static Arguments tabulations() { - return of("Tabulation should be preserved", - OfficeStamperConfigurations.standard(), - name("Homer Simpson"), - getResource(Path.of("TabsIndentationTest.docx")), - """ - ❬❬Tab❘lang=en-US❭❬|TAB|❘lang=en-US❭❬Homer Simpson❘lang=en-US❭❘lang=en-US❭ - ❬❬Space❘lang=en-US❭❬ ❘lang=en-US❭❬Homer Simpson❘lang=en-US❭❘lang=en-US❭"""); + private static Arguments replaceWordWithIntegrationTest() { + return of("replaceWordWithIntegrationTest", + OfficeStamperConfigurations.standard(), + name("Simpsons"), + getResource(Path.of("integration", + "ReplaceWordWithIntegrationTest.docx")), + """ + ReplaceWordWith Integration + ❬This variable ❬name❘b=true❭❬ ❘b=true❭should be resolved to the value Simpsons.❘b=true❭ + This variable ❬name❘b=true❭ should be resolved to the value Simpsons. + """); } private static Arguments replaceNullExpressionTest() { return of("Do not replace 'null' values", - OfficeStamperConfigurations.standard() - .addResolver(Resolvers.nullToPlaceholder()), - name(null), - getResource(Path.of("ReplaceNullExpressionTest.docx")), - "I am ${name}."); - } - - private static Arguments replaceNullExpressionTest2() { - return of("Do replace 'null' values", - OfficeStamperConfigurations.standard() - .addResolver(Resolvers.nullToEmpty()), - name(null), - getResource(Path.of("ReplaceNullExpressionTest.docx")), - "I am ."); + OfficeStamperConfigurations.standard() + .addResolver(Resolvers.nullToPlaceholder()), + name(null), + getResource(Path.of("ReplaceNullExpressionTest.docx")), + "I am ${name}."); } private static Arguments repeatTableRowKeepsFormatTest() { return of("repeatTableRowKeepsFormatTest", - OfficeStamperConfigurations.standard(), - new Show(List.of(new CharacterRecord(1, - "st", - "Homer Simpson", - "Dan Castellaneta"), - new CharacterRecord(2, - "nd", - "Marge Simpson", - "Julie Kavner"), - new CharacterRecord(3, - "rd", - "Bart Simpson", - "Nancy Cartwright"), - new CharacterRecord(4, - "th", - "Lisa Simpson", - "Yeardley Smith"), - new CharacterRecord(5, - "th", - "Maggie Simpson", - "Julie Kavner"))), - getResource(Path.of("integration", - "RepeatTableRowKeepsFormatTest.docx")), - """ - 1❬st❘vertAlign=superscript❭ Homer Simpson-❬Dan Castellaneta❘b=true❭ - 2❬nd❘vertAlign=superscript❭ Marge Simpson-❬Julie Kavner❘b=true❭ - 3❬rd❘vertAlign=superscript❭ Bart Simpson-❬Nancy Cartwright❘b=true❭ - 4❬th❘vertAlign=superscript❭ Lisa Simpson-❬Yeardley Smith❘b=true❭ - 5❬th❘vertAlign=superscript❭ Maggie Simpson-❬Julie Kavner❘b=true❭ - """); + OfficeStamperConfigurations.standard(), + new Show(List.of(new CharacterRecord(1, + "st", + "Homer Simpson", + "Dan Castellaneta"), + new CharacterRecord(2, + "nd", + "Marge Simpson", + "Julie Kavner"), + new CharacterRecord(3, + "rd", + "Bart Simpson", + "Nancy Cartwright"), + new CharacterRecord(4, + "th", + "Lisa Simpson", + "Yeardley Smith"), + new CharacterRecord(5, + "th", + "Maggie Simpson", + "Julie Kavner"))), + getResource(Path.of("integration", + "RepeatTableRowKeepsFormatTest.docx")), + """ + 1❬st❘vertAlign=superscript❭ Homer Simpson-❬Dan Castellaneta❘b=true❭ + 2❬nd❘vertAlign=superscript❭ Marge Simpson-❬Julie Kavner❘b=true❭ + 3❬rd❘vertAlign=superscript❭ Bart Simpson-❬Nancy Cartwright❘b=true❭ + 4❬th❘vertAlign=superscript❭ Lisa Simpson-❬Yeardley Smith❘b=true❭ + 5❬th❘vertAlign=superscript❭ Maggie Simpson-❬Julie Kavner❘b=true❭ + """); } private static Arguments repeatParagraphTest() { @@ -209,19 +234,19 @@ private static Arguments repeatParagraphTest() { ❬There are ❬6❘lang=de-DE❭ characters.❘spacing={after=140,before=0}❭"""; return arguments("repeatParagraphTest", - OfficeStamperConfigurations.standard(), - context, - template, - expected); + OfficeStamperConfigurations.standard(), + context, + template, + expected); } private static Arguments repeatDocPartWithImageTestShouldImportImageDataInTheMainDocument() { var context = Map.of("units", Stream.of(getImage(Path.of("butterfly" + - ".png")), - getImage(Path.of("map.jpg"))) - .map(image -> Map.of("coverImage", image)) - .map(map -> Map.of("productionFacility", map)) - .toList()); + ".png")), + getImage(Path.of("map.jpg"))) + .map(image -> Map.of("coverImage", image)) + .map(map -> Map.of("productionFacility", map)) + .toList()); var template = getResource(Path.of("RepeatDocPartWithImageTest.docx")); var expected = """ @@ -235,8 +260,8 @@ private static Arguments repeatDocPartWithImageTestShouldImportImageDataInTheMai """; var config = OfficeStamperConfigurations.standard() - .setEvaluationContextConfigurer( - ctx -> ctx.addPropertyAccessor(new MapAccessor())); + .setEvaluationContextConfigurer( + ctx -> ctx.addPropertyAccessor(new MapAccessor())); return of( "repeatDocPartWithImageTestShouldImportImageDataInTheMainDocument", config, @@ -245,23 +270,15 @@ private static Arguments repeatDocPartWithImageTestShouldImportImageDataInTheMai expected); } - private static Image getImage(Path path) { - try { - return new Image(getResource(path)); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - private static Arguments repeatDocPartWithImagesInSourceTestshouldReplicateImageFromTheMainDocumentInTheSubTemplate() { return of( "repeatDocPartWithImagesInSourceTestshouldReplicateImageFromTheMainDocumentInTheSubTemplate", OfficeStamperConfigurations.standard() - .setEvaluationContextConfigurer( - (ctx) -> ctx.addPropertyAccessor(new MapAccessor())), + .setEvaluationContextConfigurer( + (ctx) -> ctx.addPropertyAccessor(new MapAccessor())), Contexts.subDocPartContext(), getResource(Path.of("RepeatDocPartWithImagesInSourceTest" + - ".docx")), + ".docx")), """ This is not repeated This should be repeated : first doc part @@ -273,207 +290,214 @@ private static Arguments repeatDocPartWithImagesInSourceTestshouldReplicateImage This is not repeated"""); } + private static Image getImage(Path path) { + try { + return new Image(getResource(path)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + private static Arguments repeatDocPartTest() { return of("repeatDocPartTest", - OfficeStamperConfigurations.standard(), - new Characters(List.of( - new Role("Homer Simpson", "Dan Castellaneta"), - new Role("Marge Simpson", "Julie Kavner"), - new Role("Bart Simpson", "Nancy Cartwright"), - new Role("Kent Brockman", "Harry Shearer"), - new Role("Disco Stu", "Hank Azaria"), - new Role("Krusty the Clown", "Dan Castellaneta"))), - getResource(Path.of("RepeatDocPartTest.docx")), - """ - Repeating Doc Part - ❬❬List ❘b=true❭❬of❘b=true❭❬ Simpsons ❘b=true❭❬characters❘b=true❭❘b=true,spacing={after=120,before=240}❭ - ❬Paragraph for test: Homer Simpson - Dan Castellaneta❘spacing={after=120,before=240}❭ - ❬Homer Simpson❘jc=center❭ - ❬Dan Castellaneta❘jc=center❭ - ❬ |BR|❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬Paragraph for test: Marge Simpson - Julie Kavner❘spacing={after=120,before=240}❭ - ❬Marge Simpson❘jc=center❭ - ❬Julie Kavner❘jc=center❭ - ❬ |BR|❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬Paragraph for test: Bart Simpson - Nancy Cartwright❘spacing={after=120,before=240}❭ - ❬Bart Simpson❘jc=center❭ - ❬Nancy Cartwright❘jc=center❭ - ❬ |BR|❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬Paragraph for test: Kent Brockman - Harry Shearer❘spacing={after=120,before=240}❭ - ❬Kent Brockman❘jc=center❭ - ❬Harry Shearer❘jc=center❭ - ❬ |BR|❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬Paragraph for test: Disco Stu - Hank Azaria❘spacing={after=120,before=240}❭ - ❬Disco Stu❘jc=center❭ - ❬Hank Azaria❘jc=center❭ - ❬ |BR|❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬Paragraph for test: Krusty the Clown - Dan Castellaneta❘spacing={after=120,before=240}❭ - ❬Krusty the Clown❘jc=center❭ - ❬Dan Castellaneta❘jc=center❭ - ❬ |BR|❘suppressAutoHyphens=xxx,widowControl=xxx❭ - There are 6 characters."""); + OfficeStamperConfigurations.standard(), + new Characters(List.of( + new Role("Homer Simpson", "Dan Castellaneta"), + new Role("Marge Simpson", "Julie Kavner"), + new Role("Bart Simpson", "Nancy Cartwright"), + new Role("Kent Brockman", "Harry Shearer"), + new Role("Disco Stu", "Hank Azaria"), + new Role("Krusty the Clown", "Dan Castellaneta"))), + getResource(Path.of("RepeatDocPartTest.docx")), + """ + Repeating Doc Part + ❬❬List ❘b=true❭❬of❘b=true❭❬ Simpsons ❘b=true❭❬characters❘b=true❭❘b=true,spacing={after=120,before=240}❭ + ❬Paragraph for test: Homer Simpson - Dan Castellaneta❘spacing={after=120,before=240}❭ + ❬Homer Simpson❘jc=center❭ + ❬Dan Castellaneta❘jc=center❭ + ❬ |BR(PAGE)|❘suppressAutoHyphens=xxx,widowControl=xxx❭ + ❬Paragraph for test: Marge Simpson - Julie Kavner❘spacing={after=120,before=240}❭ + ❬Marge Simpson❘jc=center❭ + ❬Julie Kavner❘jc=center❭ + ❬ |BR(PAGE)|❘suppressAutoHyphens=xxx,widowControl=xxx❭ + ❬Paragraph for test: Bart Simpson - Nancy Cartwright❘spacing={after=120,before=240}❭ + ❬Bart Simpson❘jc=center❭ + ❬Nancy Cartwright❘jc=center❭ + ❬ |BR(PAGE)|❘suppressAutoHyphens=xxx,widowControl=xxx❭ + ❬Paragraph for test: Kent Brockman - Harry Shearer❘spacing={after=120,before=240}❭ + ❬Kent Brockman❘jc=center❭ + ❬Harry Shearer❘jc=center❭ + ❬ |BR(PAGE)|❘suppressAutoHyphens=xxx,widowControl=xxx❭ + ❬Paragraph for test: Disco Stu - Hank Azaria❘spacing={after=120,before=240}❭ + ❬Disco Stu❘jc=center❭ + ❬Hank Azaria❘jc=center❭ + ❬ |BR(PAGE)|❘suppressAutoHyphens=xxx,widowControl=xxx❭ + ❬Paragraph for test: Krusty the Clown - Dan Castellaneta❘spacing={after=120,before=240}❭ + ❬Krusty the Clown❘jc=center❭ + ❬Dan Castellaneta❘jc=center❭ + ❬ |BR(PAGE)|❘suppressAutoHyphens=xxx,widowControl=xxx❭ + There are 6 characters."""); } private static Arguments repeatDocPartNestingTest() { return of("repeatDocPartNestingTest", - OfficeStamperConfigurations.standard(), - Contexts.schoolContext(), - getResource(Path.of("RepeatDocPartNestingTest.docx")), - """ - ❬Repeating ❬N❘lang=en-US❭ested Doc Part ❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬List of All the s❬tu❘lang=en-US❭❬dent’s of all grades❘lang=null❭❘lang=null,suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬❬South Park Primary School❘lang=null❭❘lang=null,suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬❬Grade No.❘b=true,lang=null❭❬0❘b=true,lang=null❭❬ ❘b=true,lang=null❭❬t❘lang=null❭here are 3 classes❘b=true,lang=null,suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬Class No.0 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Class No.1 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Class No.2 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬❬Grade No.❘b=true,lang=null❭❬1❘b=true,lang=null❭❬ ❘b=true,lang=null❭❬t❘lang=null❭here are 3 classes❘b=true,lang=null,suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬Class No.0 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Class No.1 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Class No.2 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬❬Grade No.❘b=true,lang=null❭❬2❘b=true,lang=null❭❬ ❘b=true,lang=null❭❬t❘lang=null❭here are 3 classes❘b=true,lang=null,suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬Class No.0 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Class No.1 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Class No.2 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ - ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ - There are 3 grades."""); + OfficeStamperConfigurations.standard(), + Contexts.schoolContext(), + getResource(Path.of("RepeatDocPartNestingTest.docx")), + """ + ❬Repeating ❬N❘lang=en-US❭ested Doc Part ❘suppressAutoHyphens=xxx,widowControl=xxx❭ + ❬List of All the s❬tu❘lang=en-US❭❬dent’s of all grades❘lang=null❭❘lang=null,suppressAutoHyphens=xxx,widowControl=xxx❭ + ❬❬South Park Primary School❘lang=null❭❘lang=null,suppressAutoHyphens=xxx,widowControl=xxx❭ + ❬❬Grade No.❘b=true,lang=null❭❬0❘b=true,lang=null❭❬ ❘b=true,lang=null❭❬t❘lang=null❭here are 3 classes❘b=true,lang=null,suppressAutoHyphens=xxx,widowControl=xxx❭ + ❬Class No.0 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ + ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Class No.1 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ + ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Class No.2 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ + ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬❬Grade No.❘b=true,lang=null❭❬1❘b=true,lang=null❭❬ ❘b=true,lang=null❭❬t❘lang=null❭here are 3 classes❘b=true,lang=null,suppressAutoHyphens=xxx,widowControl=xxx❭ + ❬Class No.0 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ + ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Class No.1 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ + ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Class No.2 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ + ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬❬Grade No.❘b=true,lang=null❭❬2❘b=true,lang=null❭❬ ❘b=true,lang=null❭❬t❘lang=null❭here are 3 classes❘b=true,lang=null,suppressAutoHyphens=xxx,widowControl=xxx❭ + ❬Class No.0 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ + ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Class No.1 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ + ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Class No.2 ❬t❘lang=null❭here are 5 students❘suppressAutoHyphens=xxx,widowControl=xxx❭ + ❬0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No0❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No1❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No2❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No3❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬Bruce·No4❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + ❬5❘ind=0,jc=left,keepLines=false,keepNext=true,outlineLvl=9,pageBreakBefore=false,spacing={after=120,before=240,line=15,lineRule=AUTO},suppressAutoHyphens=xxx,textAlignment=xxx,topLinePunct=xxx,widowControl=xxx,wordWrap=xxx❭ + There are 3 grades."""); } - private static Arguments repeatDocPartAndCommentProcessorsIsolationTest_repeatDocPartShouldNotUseSameCommentProcessorInstancesForSubtemplate() { var context = Contexts.tableContext(); var template = getResource( @@ -504,8 +528,8 @@ private static Arguments repeatDocPartAndCommentProcessorsIsolationTest_repeatDo This will stay untouched too."""; var config = OfficeStamperConfigurations.standard() - .setEvaluationContextConfigurer( - (ctx) -> ctx.addPropertyAccessor(new MapAccessor())); + .setEvaluationContextConfigurer( + (ctx) -> ctx.addPropertyAccessor(new MapAccessor())); return arguments( "RepeatDocPartAndCommentProcessorsIsolationTest_repeatDocPartShouldNotUseSameCommentProcessorInstancesForSubtemplate", @@ -519,22 +543,22 @@ private static Arguments changingPageLayoutTest_shouldKeepSectionBreakOrientatio return arguments( "changingPageLayoutTest_shouldKeepSectionBreakOrientationInRepeatParagraphWithoutSectionBreakInsideComment", OfficeStamperConfigurations.standard() - .setEvaluationContextConfigurer( - ctx -> ctx.addPropertyAccessor(new MapAccessor())), + .setEvaluationContextConfigurer( + ctx -> ctx.addPropertyAccessor(new MapAccessor())), Map.of("repeatValues", - List.of(new Name("Homer"), new Name("Marge"))), + List.of(new Name("Homer"), new Name("Marge"))), getResource(Path.of( "ChangingPageLayoutOutsideRepeatParagraphTest.docx")), """ First page is landscape. - + ❬❘docGrid=xxx,eGHdrFtrReferences=xxx,pgMar=xxx,pgSz={h=11906,orient=landscape,w=16838}❭ Second page is portrait, layout change should survive to repeatParagraph processor (Homer). - - Without a section break changing the layout in between, but a page break instead.|BR| + + Without a section break changing the layout in between, but a page break instead.|BR(PAGE)| Second page is portrait, layout change should survive to repeatParagraph processor (Marge). - - Without a section break changing the layout in between, but a page break instead.|BR| + + Without a section break changing the layout in between, but a page break instead.|BR(PAGE)| ❬❘docGrid=xxx,eGHdrFtrReferences=xxx,pgMar=xxx,pgSz={h=16838,w=11906}❭ Fourth page is set to landscape again."""); } @@ -559,8 +583,8 @@ Second page is portrait, layout change should survive to repeatParagraph process Fourth page is set to portrait again."""; var config = OfficeStamperConfigurations.standard() - .setEvaluationContextConfigurer( - ctx -> ctx.addPropertyAccessor(new MapAccessor())); + .setEvaluationContextConfigurer( + ctx -> ctx.addPropertyAccessor(new MapAccessor())); return arguments( "changingPageLayoutTest_shouldKeepSectionBreakOrientationInRepeatParagraphWithSectionBreakInsideComment", config, @@ -569,59 +593,67 @@ Second page is portrait, layout change should survive to repeatParagraph process expected); } - private static Arguments changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithSectionBreaksInsideComment() { return arguments( "changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithSectionBreaksInsideComment", OfficeStamperConfigurations.standard() - .setEvaluationContextConfigurer( - ctx -> ctx.addPropertyAccessor(new MapAccessor())), + .setEvaluationContextConfigurer( + ctx -> ctx.addPropertyAccessor(new MapAccessor())), Map.of("repeatValues", - List.of(new Name("Homer"), new Name("Marge"))), + List.of(new Name("Homer"), new Name("Marge"))), getResource(Path.of("ChangingPageLayoutInRepeatDocPartTest" + - ".docx")), + ".docx")), """ First page is portrait. - + ❬❘docGrid=xxx,eGHdrFtrReferences=xxx,pgMar=xxx,pgSz={h=16838,w=11906}❭ Second page is landscape, layout change should survive to repeatDocPart (Homer). - + ❬❘docGrid=xxx,eGHdrFtrReferences=xxx,pgMar=xxx,pgSz={h=11906,orient=landscape,w=16838}❭ ❬With a break setting the layout to portrait in between.❘docGrid=xxx,eGHdrFtrReferences=xxx,pgMar=xxx,pgSz={h=16838,w=11906}❭ Second page is landscape, layout change should survive to repeatDocPart (Marge). - + ❬❘docGrid=xxx,eGHdrFtrReferences=xxx,pgMar=xxx,pgSz={h=11906,orient=landscape,w=16838}❭ ❬With a break setting the layout to portrait in between.❘docGrid=xxx,eGHdrFtrReferences=xxx,pgMar=xxx,pgSz={h=16838,w=11906}❭ ❬❘docGrid=xxx,eGHdrFtrReferences=xxx,pgMar=xxx,pgSz={h=16838,w=11906}❭ Fourth page is set to landscape again."""); } + private static Arguments replaceNullExpressionTest2() { + return of("Do replace 'null' values", + OfficeStamperConfigurations.standard() + .addResolver(Resolvers.nullToEmpty()), + name(null), + getResource(Path.of("ReplaceNullExpressionTest.docx")), + "I am ."); + } + private static Arguments changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithSectionBreaksInsideCommentAndTableAsLastElement() { return arguments( "changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithSectionBreaksInsideCommentAndTableAsLastElement", OfficeStamperConfigurations.standard() - .setEvaluationContextConfigurer( - ctx -> ctx.addPropertyAccessor(new MapAccessor())), + .setEvaluationContextConfigurer( + ctx -> ctx.addPropertyAccessor(new MapAccessor())), Map.of("repeatValues", - List.of(new Name("Homer"), new Name("Marge"))), + List.of(new Name("Homer"), new Name("Marge"))), getResource( Path.of( "ChangingPageLayoutInRepeatDocPartWithTableLastElementTest.docx")), """ First page is portrait. - + ❬❘docGrid=xxx,eGHdrFtrReferences=xxx,pgMar=xxx,pgSz={h=16838,w=11906}❭ Second page is landscape, layout change should survive to repeatDocPart (Homer). - + ❬❘docGrid=xxx,eGHdrFtrReferences=xxx,pgMar=xxx,pgSz={h=11906,orient=landscape,w=16838}❭ With a break setting the layout to portrait in between. - + ❬❘docGrid=xxx,eGHdrFtrReferences=xxx,pgMar=xxx,pgSz={h=16838,w=11906}❭ Second page is landscape, layout change should survive to repeatDocPart (Marge). - + ❬❘docGrid=xxx,eGHdrFtrReferences=xxx,pgMar=xxx,pgSz={h=11906,orient=landscape,w=16838}❭ With a break setting the layout to portrait in between. - + ❬❘docGrid=xxx,eGHdrFtrReferences=xxx,pgMar=xxx,pgSz={h=16838,w=11906}❭ ❬❘docGrid=xxx,eGHdrFtrReferences=xxx,pgMar=xxx,pgSz={h=16838,w=11906}❭ Fourth page is set to landscape again."""); @@ -631,21 +663,21 @@ private static Arguments changingPageLayoutTest_shouldKeepPageBreakOrientationIn return arguments( "changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithoutSectionBreaksInsideComment", OfficeStamperConfigurations.standard() - .setEvaluationContextConfigurer( - ctx -> ctx.addPropertyAccessor(new MapAccessor())), + .setEvaluationContextConfigurer( + ctx -> ctx.addPropertyAccessor(new MapAccessor())), Map.of("repeatValues", - List.of(new Name("Homer"), new Name("Marge"))), + List.of(new Name("Homer"), new Name("Marge"))), getResource(Path.of( "ChangingPageLayoutOutsideRepeatDocPartTest.docx")), """ First page is landscape. - + ❬❘docGrid=xxx,eGHdrFtrReferences=xxx,pgMar=xxx,pgSz={h=11906,orient=landscape,w=16838}❭ Second page is portrait, layout change should survive to repeatDocPart (Homer). - |BR| + |BR(PAGE)| Without a break changing the layout in between (page break should be repeated). Second page is portrait, layout change should survive to repeatDocPart (Marge). - |BR| + |BR(PAGE)| Without a break changing the layout in between (page break should be repeated). ❬❘docGrid=xxx,eGHdrFtrReferences=xxx,pgMar=xxx,pgSz={h=16838,w=11906}❭ Fourth page is set to landscape again."""); @@ -728,7 +760,7 @@ private static Arguments conditionalDisplayOfParagraphsTest_unresolvedInlineProc private static Arguments conditionalDisplayOfTableRowsTest() { var context = new Contexts.Name("Homer"); var template = getResource(Path.of("ConditionalDisplayOfTableRowsTest" + - ".docx")); + ".docx")); var expected = """ ❬Conditional Display of Table Rows❘spacing={after=120,before=240}❭ ❬❬This paragraph stays untouched.❘lang=de-DE❭❘lang=de-DE❭ @@ -739,10 +771,10 @@ private static Arguments conditionalDisplayOfTableRowsTest() { ❬❘spacing={after=140,before=0}❭"""; return arguments("conditionalDisplayOfTableRowsTest", - OfficeStamperConfigurations.standard(), - context, - template, - expected); + OfficeStamperConfigurations.standard(), + context, + template, + expected); } private static Arguments conditionalDisplayOfTableBug32Test() { @@ -763,16 +795,16 @@ private static Arguments conditionalDisplayOfTableBug32Test() { ❬❬This paragraph stays untouched.❘lang=de-DE❭❘spacing={after=140,before=0}❭"""; return arguments("conditionalDisplayOfTableBug32Test", - OfficeStamperConfigurations.standard(), - context, - template, - expected); + OfficeStamperConfigurations.standard(), + context, + template, + expected); } private static Arguments conditionalDisplayOfTableTest() { var context = new Contexts.Name("Homer"); var template = getResource(Path.of("ConditionalDisplayOfTablesTest" + - ".docx")); + ".docx")); var expected = """ ❬Conditional Display of Tables❘spacing={after=120,before=240}❭ ❬❬This paragraph stays untouched.❘lang=de-DE❭❘lang=de-DE❭ @@ -787,10 +819,10 @@ private static Arguments conditionalDisplayOfTableTest() { ❬❬This paragraph stays untouched.❘lang=de-DE❭❘lang=de-DE,spacing={after=140,before=0}❭"""; return arguments("conditionalDisplayOfTablesTest", - OfficeStamperConfigurations.standard(), - context, - template, - expected); + OfficeStamperConfigurations.standard(), + context, + template, + expected); } private static Arguments customEvaluationContextConfigurerTest_customEvaluationContextConfigurerIsHonored() { @@ -802,10 +834,10 @@ private static Arguments customEvaluationContextConfigurerTest_customEvaluationC ❬❬This paragraph stays untouched.❘lang=de-DE❭❘lang=de-DE❭ ❬The variable foo has the value ❬bar❘lang=de-DE❭.❘spacing={after=140,before=0}❭"""; var config = OfficeStamperConfigurations.standard() - .setEvaluationContextConfigurer( - evalContext -> evalContext.addPropertyAccessor(new SimpleGetter( - "foo", - "bar"))); + .setEvaluationContextConfigurer( + evalContext -> evalContext.addPropertyAccessor(new SimpleGetter( + "foo", + "bar"))); return arguments( "customEvaluationContextConfigurerTest_customEvaluationContextConfigurerIsHonored", @@ -819,32 +851,32 @@ private static Arguments customExpressionFunctionTest() { var context = new Contexts.Name("Homer Simpson"); var template = getResource(Path.of("CustomExpressionFunction.docx")); var expected = """ - ❬Custom Expression Function❘spacing={after=120,before=240}❭ - ❬❬This paragraph is untouched.❘lang=de-DE❭❘lang=de-DE❭ - ❬❬In this paragraph, a custom expression function is used to uppercase a String: |BR|❘lang=de-DE❭❬HOMER SIMPSON❘b=true,lang=de-DE❭❬.❘lang=de-DE❭❘spacing={after=140,before=0}❭ - ❬❬To test that custom functions work together with comment expressions, we toggle visibility of this paragraph with a comment expression.❘lang=de-DE❭❘spacing={after=140,before=0}❭"""; + Custom Expression Function + This paragraph is untouched. + In this paragraph, a custom expression function is used to uppercase a String: ❬HOMER SIMPSON❘b=true❭❬.❘b=true❭ + To test that custom functions work together with comment expressions, we toggle visibility of this paragraph with a comment expression."""; var config = OfficeStamperConfigurations.standard() - .exposeInterfaceToExpressionLanguage( - Functions.UppercaseFunction.class, - Functions.upperCase()); + .exposeInterfaceToExpressionLanguage( + Functions.UppercaseFunction.class, + Functions.upperCase()); return arguments("customExpressionFunctionTest", - config, - context, - template, - expected); + config, + context, + template, + expected); } private static Arguments customTypeResolverTest() { return arguments("customTypeResolverTest", - OfficeStamperConfigurations.standard() - .addResolver(new CustomTypeResolver()), - new Context(new CustomType()), - getResource(Path.of("CustomTypeResolverTest.docx")), - """ - ❬Custom TypeResolver Test❘spacing={after=120,before=240}❭ - ❬This paragraph is untouched.❘lang=de-DE❭ - ❬The name should be resolved to ❘lang=de-DE❭❬foo❘b=true,lang=de-DE❭❬.❘lang=de-DE❭ - ❬❬This paragraph is untouched.❘lang=de-DE❭❘spacing={after=140,before=0}❭"""); + OfficeStamperConfigurations.standard() + .addResolver(new CustomTypeResolver()), + new Context(new CustomType()), + getResource(Path.of("CustomTypeResolverTest.docx")), + """ + ❬Custom TypeResolver Test❘spacing={after=120,before=240}❭ + ❬This paragraph is untouched.❘lang=de-DE❭ + ❬The name should be resolved to ❘lang=de-DE❭❬foo❘b=true,lang=de-DE❭❬.❘lang=de-DE❭ + ❬❬This paragraph is untouched.❘lang=de-DE❭❘spacing={after=140,before=0}❭"""); } private static Arguments dateReplacementTest() { @@ -857,10 +889,10 @@ private static Arguments dateReplacementTest() { Today is: %s""".formatted(formattedDate); return arguments("dateReplacementTest", - OfficeStamperConfigurations.standard(), - context, - template, - expected); + OfficeStamperConfigurations.standard(), + context, + template, + expected); } private static Arguments expressionReplacementInGlobalParagraphsTest() { @@ -873,18 +905,18 @@ private static Arguments expressionReplacementInGlobalParagraphsTest() { ❬In this paragraph, the variable ❘lang=de-DE❭❬name❘b=true,lang=de-DE❭ should be resolved to the value ❬Homer Simpson❘lang=de-DE❭. ❬❬In this paragraph, the variable ❘lang=de-DE❭❬foo❘b=true,lang=de-DE❭❬ should not be resolved: ${foo}.❘lang=de-DE❭❘spacing={after=140,before=0}❭"""; OfficeStamperConfiguration config = OfficeStamperConfigurations.standard() - .setFailOnUnresolvedExpression(false); + .setFailOnUnresolvedExpression(false); return arguments("expressionReplacementInGlobalParagraphsTest", - config, - context, - template, - expected); + config, + context, + template, + expected); } private static Arguments expressionReplacementInTablesTest() { var context = new Contexts.Name("Bart Simpson"); var template = getResource(Path.of("ExpressionReplacementInTablesTest" + - ".docx")); + ".docx")); var expected = """ ❬Expression Replacement in Tables❘spacing={after=120,before=240}❭ @@ -900,12 +932,12 @@ private static Arguments expressionReplacementInTablesTest() { ❬❘spacing={after=140,before=0}❭"""; OfficeStamperConfiguration config = OfficeStamperConfigurations.standard() - .setFailOnUnresolvedExpression(false); + .setFailOnUnresolvedExpression(false); return arguments("expressionReplacementInTablesTest", - config, - context, - template, - expected); + config, + context, + template, + expected); } private static Arguments expressionReplacementWithFormattingTest() { @@ -931,10 +963,10 @@ private static Arguments expressionReplacementWithFormattingTest() { ❬It should be white over darkblue: ❬Homer Simpson❘color=FFFFFF,highlight=darkBlue❭❘b=true❭ ❬It should be with header formatting: ❬Homer Simpson❘rStyle=TitreCar❭❘b=true❭"""; return arguments("expressionReplacementWithFormattingTest", - OfficeStamperConfigurations.standard(), - context, - template, - expected); + OfficeStamperConfigurations.standard(), + context, + template, + expected); } private static Arguments expressionWithSurroundingSpacesTest() { @@ -952,10 +984,10 @@ private static Arguments expressionWithSurroundingSpacesTest() { Before Expression After. ❬Before Expression After.❘spacing={after=140,before=0}❭"""; return arguments("expressionWithSurroundingSpacesTest", - OfficeStamperConfigurations.standard(), - spacyContext, - template, - expected); + OfficeStamperConfigurations.standard(), + spacyContext, + template, + expected); } private static Arguments expressionReplacementWithCommentTest() { @@ -968,12 +1000,12 @@ private static Arguments expressionReplacementWithCommentTest() { In this paragraph, the variable ❬name❘b=true❭ should be resolved to the value Homer Simpson. ❬In this paragraph, the variable ❬foo❘b=true❭ should not be resolved: unresolvedValueWithCommentreplaceWordWith(foo).❘spacing={after=140,before=0,line=288,lineRule=AUTO}❭"""; var config = OfficeStamperConfigurations.standard() - .setFailOnUnresolvedExpression(false); + .setFailOnUnresolvedExpression(false); return arguments("expressionReplacementWithCommentsTest", - config, - context, - template, - expected); + config, + context, + template, + expected); } /** @@ -981,7 +1013,7 @@ private static Arguments expressionReplacementWithCommentTest() { */ private static Arguments imageReplacementInGlobalParagraphsTest() { var context = new Contexts.ImageContext(getImage(Path.of("monalisa" + - ".jpg"))); + ".jpg"))); var template = getResource(Path.of( "ImageReplacementInGlobalParagraphsTest.docx")); var expected = """ @@ -990,16 +1022,16 @@ private static Arguments imageReplacementInGlobalParagraphsTest() { ❬In this paragraph, an image of Mona Lisa is inserted: ❬rId4:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:1276350❘lang=de-DE❭.❘lang=de-DE❭ ❬This paragraph has the image ❬rId5:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:1276350❘lang=de-DE❭ in the middle.❘lang=de-DE,spacing={after=140,before=0}❭"""; return arguments("imageReplacementInGlobalParagraphsTest", - OfficeStamperConfigurations.standard(), - context, - template, - expected); + OfficeStamperConfigurations.standard(), + context, + template, + expected); } private static Arguments imageReplacementInGlobalParagraphsTestWithMaxWidth() { var context = new Contexts.ImageContext(getImage(Path.of("monalisa" + - ".jpg"), - 1000)); + ".jpg"), + 1000)); var template = getResource(Path.of( "ImageReplacementInGlobalParagraphsTest.docx")); var expected = """ @@ -1008,10 +1040,10 @@ private static Arguments imageReplacementInGlobalParagraphsTestWithMaxWidth() { ❬In this paragraph, an image of Mona Lisa is inserted: ❬rId4:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:635000❘lang=de-DE❭.❘lang=de-DE❭ ❬This paragraph has the image ❬rId5:image/jpeg:8.8kB:sha1=XMpVtDbetKjZTkPhy598GdJQM/4=:cy=$d:635000❘lang=de-DE❭ in the middle.❘lang=de-DE,spacing={after=140,before=0}❭"""; return arguments("imageReplacementInGlobalParagraphsTestWithMaxWidth", - OfficeStamperConfigurations.standard(), - context, - template, - expected); + OfficeStamperConfigurations.standard(), + context, + template, + expected); } private static Image getImage(Path path, int size) { @@ -1025,35 +1057,35 @@ private static Image getImage(Path path, int size) { private static Arguments leaveEmptyOnExpressionErrorTest() { var context = new Contexts.Name("Homer Simpson"); var template = getResource(Path.of("LeaveEmptyOnExpressionErrorTest" + - ".docx")); + ".docx")); var expected = """ Leave me empty . ❬❘u=single❭"""; var config = OfficeStamperConfigurations.standard() - .setFailOnUnresolvedExpression(false) - .leaveEmptyOnExpressionError(true); + .setFailOnUnresolvedExpression(false) + .leaveEmptyOnExpressionError(true); return arguments("leaveEmptyOnExpressionErrorTest", - config, - context, - template, - expected); + config, + context, + template, + expected); } private static Arguments lineBreakReplacementTest() { var config = OfficeStamperConfigurations.standard() - .setLineBreakPlaceholder("#"); + .setLineBreakPlaceholder("#"); var context = new Contexts.Name(null); var template = getResource(Path.of("LineBreakReplacementTest.docx")); var expected = """ ❬❬Line Break Replacement❘lang=en-US❭❘lang=en-US❭ ❬❬This paragraph is untouched.❘lang=en-US❭❘lang=en-US❭ - ❬This paragraph should be ❬|BR|❘lang=en-US❭ split in ❬|BR|❘lang=en-US❭❬ three❘lang=en-US❭❬ lines.❘lang=en-US❭❘lang=en-US❭ + ❬This paragraph should be ❬|BR(null)|❘lang=en-US❭ split in ❬|BR(null)|❘lang=en-US❭❬ three❘lang=en-US❭❬ lines.❘lang=en-US❭❘lang=en-US❭ ❬❬This paragraph is untouched.❘lang=en-US❭❘lang=en-US❭"""; return arguments("lineBreakReplacementTest", - config, - context, - template, - expected); + config, + context, + template, + expected); } private static Arguments mapAccessorAndReflectivePropertyAccessorTest_shouldResolveMapAndPropertyPlaceholders() { @@ -1062,16 +1094,16 @@ private static Arguments mapAccessorAndReflectivePropertyAccessorTest_shouldReso Path.of("MapAccessorAndReflectivePropertyAccessorTest.docx")); var expected = """ Flat string : Flat string has been resolved - + Values - + first value - - + + second value - - - + + + Paragraph start first value Paragraph end @@ -1081,13 +1113,13 @@ private static Arguments mapAccessorAndReflectivePropertyAccessorTest_shouldReso """; var config = OfficeStamperConfigurations.standard() - .setFailOnUnresolvedExpression(false) - .setLineBreakPlaceholder("\n") - .addResolver(Resolvers.nullToDefault("N/C")) - .replaceUnresolvedExpressions(true) - .unresolvedExpressionsDefaultValue("N/C") - .setEvaluationContextConfigurer(ctx -> ctx.addPropertyAccessor( - new MapAccessor())); + .setFailOnUnresolvedExpression(false) + .setLineBreakPlaceholder("\n") + .addResolver(Resolvers.nullToDefault("N/C")) + .replaceUnresolvedExpressions(true) + .unresolvedExpressionsDefaultValue("N/C") + .setEvaluationContextConfigurer(ctx -> ctx.addPropertyAccessor( + new MapAccessor())); return arguments( "mapAccessorAndReflectivePropertyAccessorTest_shouldResolveMapAndPropertyPlaceholders", @@ -1115,29 +1147,13 @@ private static Arguments nullPointerResolutionTest_testWithDefaultSpel() { """; var config = OfficeStamperConfigurations.standard() - .setFailOnUnresolvedExpression(false); + .setFailOnUnresolvedExpression(false); return arguments("nullPointerResolutionTest_testWithDefaultSpel", - config, - context, - template, - expected); - } - - private static Arguments customCommentProcessor() { - return arguments( - "customCommentProcessor", - OfficeStamperConfigurations.standard() - .addCommentProcessor( - ICustomCommentProcessor.class, - CustomCommentProcessor::new), - Contexts.empty(), - getResource(Path.of("CustomCommentProcessorTest.docx")), - """ - Custom CommentProcessor Test - Visited - This paragraph is untouched. - Visited"""); + config, + context, + template, + expected); } private static Arguments nullPointerResolutionTest_testWithCustomSpel() { @@ -1161,66 +1177,49 @@ private static Arguments nullPointerResolutionTest_testWithCustomSpel() { // so it will not work if your type has no default constructor and no setters. var config = OfficeStamperConfigurations.standard() - .setSpelParserConfiguration(new SpelParserConfiguration(true, - true)) - .setEvaluationContextConfigurer(EvaluationContextConfigurers.noopConfigurer()) - .addResolver(Resolvers.nullToDefault("Nullish value!!")); + .setSpelParserConfiguration(new SpelParserConfiguration(true, + true)) + .setEvaluationContextConfigurer(EvaluationContextConfigurers.noopConfigurer()) + .addResolver(Resolvers.nullToDefault("Nullish value!!")); return arguments("nullPointerResolutionTest_testWithCustomSpel", - config, - context, - template, - expected); + config, + context, + template, + expected); + } + + private static Arguments customCommentProcessor() { + return arguments( + "customCommentProcessor", + OfficeStamperConfigurations.standard() + .addCommentProcessor( + ICustomCommentProcessor.class, + CustomCommentProcessor::new), + Contexts.empty(), + getResource(Path.of("CustomCommentProcessorTest.docx")), + """ + Custom CommentProcessor Test + Visited + This paragraph is untouched. + Visited"""); } /** - *

tests.

+ *

getResource.

* - * @return a {@link java.util.stream.Stream} object + * @param path a {@link java.nio.file.Path} object + * + * @return a {@link java.io.InputStream} object */ - public static Stream tests() { - return Stream.of(tabulations(), - whitespaces(), - ternary(), - repeatingRows(), - replaceWordWithIntegrationTest(), - replaceNullExpressionTest(), - repeatTableRowKeepsFormatTest(), - repeatParagraphTest(), - repeatDocPartWithImageTestShouldImportImageDataInTheMainDocument(), - repeatDocPartWithImagesInSourceTestshouldReplicateImageFromTheMainDocumentInTheSubTemplate(), - repeatDocPartTest(), - repeatDocPartNestingTest(), - repeatDocPartAndCommentProcessorsIsolationTest_repeatDocPartShouldNotUseSameCommentProcessorInstancesForSubtemplate(), - changingPageLayoutTest_shouldKeepSectionBreakOrientationInRepeatParagraphWithoutSectionBreakInsideComment(), - changingPageLayoutTest_shouldKeepSectionBreakOrientationInRepeatParagraphWithSectionBreakInsideComment(), - changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithSectionBreaksInsideComment(), - replaceNullExpressionTest2(), - changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithSectionBreaksInsideCommentAndTableAsLastElement(), - changingPageLayoutTest_shouldKeepPageBreakOrientationInRepeatDocPartWithoutSectionBreaksInsideComment(), - conditionalDisplayOfParagraphsTest_processorExpressionsInCommentsAreResolved(), - conditionalDisplayOfParagraphsTest_inlineProcessorExpressionsAreResolved(), - conditionalDisplayOfParagraphsTest_unresolvedInlineProcessorExpressionsAreRemoved(), - conditionalDisplayOfTableRowsTest(), - conditionalDisplayOfTableBug32Test(), - conditionalDisplayOfTableTest(), - customEvaluationContextConfigurerTest_customEvaluationContextConfigurerIsHonored(), - customExpressionFunctionTest(), - customTypeResolverTest(), - dateReplacementTest(), - expressionReplacementInGlobalParagraphsTest(), - expressionReplacementInTablesTest(), - expressionReplacementWithFormattingTest(), - expressionWithSurroundingSpacesTest(), - expressionReplacementWithCommentTest(), - imageReplacementInGlobalParagraphsTest(), - imageReplacementInGlobalParagraphsTestWithMaxWidth(), - leaveEmptyOnExpressionErrorTest(), - lineBreakReplacementTest(), - mapAccessorAndReflectivePropertyAccessorTest_shouldResolveMapAndPropertyPlaceholders(), - nullPointerResolutionTest_testWithDefaultSpel(), - nullPointerResolutionTest_testWithCustomSpel(), - customCommentProcessor()); + public static InputStream getResource(Path path) { + try { + var testRoot = Path.of("test", "sources"); + var resolve = testRoot.resolve(path); + return Files.newInputStream(resolve); + } catch (IOException e) { + throw new RuntimeException(e); + } } @MethodSource("tests") diff --git a/src/test/java/pro/verron/docxstamper/test/Stringifier.java b/src/test/java/pro/verron/docxstamper/test/Stringifier.java index d58938a9..1b5570f1 100644 --- a/src/test/java/pro/verron/docxstamper/test/Stringifier.java +++ b/src/test/java/pro/verron/docxstamper/test/Stringifier.java @@ -138,15 +138,15 @@ private WordprocessingMLPackage document() { return documentSupplier.get(); } - private String stringify(R.LastRenderedPageBreak lrpb) { + private String stringify(R.LastRenderedPageBreak ignored) { return ""; // do not render } private String stringify(Br br) { - return "|BR|"; + return "|BR(" + br.getType() + ")|"; } - private String stringify(R.Tab tab) { + private String stringify(R.Tab ignored) { return "|TAB|"; } diff --git a/test/sources/CustomExpressionFunction.docx b/test/sources/CustomExpressionFunction.docx index 2428ae5d181120a3412c9e638d71ea19bdee9827..fb082641d2f67d8c91d10062039495c25aea6824 100644 GIT binary patch literal 17227 zcmeIZ1zTN7(=JRPxVsaAySux)yF+ky4;I`ZxVyW%I|PEeyStyAd7e2llXt%B`vK=& zTwSc)yYK3?dRJ9fcUQ?tf_y{)0t12o0s{IBM6V%F;qn0ps1pnb2pI?hSX0o(+R@nB zQCG>$*4ROt#?{IaKj$MbSr!m5p#A?|{|}EqUBak&FCBvLec+q_kA?+_7P9=l;T-sb z0$8S3AZ0Al9QV)Um7V7gs1o8^Q+?R}Tn^{lgwSqGi>3)>UEpTYGC_;JM0!oA(_;=- z>>lFh;z$IHe&iPTq?!;$3>_U<;|*{Ee&)#bHNii)<**)rnV^%M^r{rpD%Jb)GZhCP zSW`jTabeEL8cF;@%&54BTyH*tYL+B%*<3hDZ}Ef1a?M2;tH>ldNZ6xQ%czRHh>NEN zz$7Tks)?anSACsohcw=`<;(RLEWcFXV=(6FppVdYv}(`lxylzYc3v@oFQkSV)-R-8 zO>4vv=;EHISXO3=E7;{7Lflf}Z{iX@UB-o{&&h)K9cV_@ht>VmF8{)C{&7>~I|;X3 z-2^G2(x8^_0erqR8|I<%O%uK88l-~WS-2N@6W72*3o^kLDCDh!D~qVH0~Hcy;aZFD zr|x&dlRFXWS#r3OE>X7^g$3fA+r?{shk-kznWKBgH-J{Yzk>kD{X<{zVz3&|0YWGP zP)BHhzUtZ=TRPCv{G$J>r~eNt=igqvEVdh+;S4X2hd7QrDj z#ifv!7tH71Ubz+)fVB_xMMtM+;-=gk(nOp#;?+ac#3b$T_Q+gyPX0^9QG zUFU4KU{d$)OkPI_#mPtf!_-kjC$Yd{9)gp`yO4J&gzk0=q0I=$Cx03e)#hf{DbL?0 zc{64u)6dSCi)ng*mv9Sr1mU(!V0uHD@K%3kq>hg2S*y|RQ#y)kVu&y&zoo*mq@yKH zi!msJ!n$btapa!cS=b*ENClY<8AAK)X`+Rs@wOkjhsn!w{#m_Fchd$tO`HGvU-ct7 zdOh$Q6bR^*6bJ|rFeolI_C~ZuHik}CfJyDwZ1yuz+h&UmCFopr=^bI$eVHf#C<2O5 zLr>X^rsVL1+1^kvXW&x}F3;O5-Z!Et*covJrdnJ1!iLH1`TYl+=eN#l5PfLljEHMJBad7a~?vL{_)xyIoga^iKfs4 zU<)oe7~;~-qCxXmNNg?>MQ}$>K;3X2I=H)yBCt5QC16J`z+X$S$PYv~YMc346)I)0 zoCzPDri-wPozqNTeI58QU@DsO#CkjJPvwZ#8SFsxzB*o|mCf`Y~Jf@*6H0+1a@cc!Y6>G4U46xz>skqv+fngDo`F~7wWf- z%{6QfV!r4!kO5+|2t0VqhnvB;Mo#R(8a%Z%4#zPJV+xHai5)-?Z-oUzwU(8Ap`Oc> zEa@bk?2AeCjJ$KCs?@xmGV7*@y{^+MnhbFZiSaFw%)KxhZxX5`C-YOP#n&w30^Xy= z;h5RUH2Qv@s+J0$re0vai0oR}Zr{dQ?)%l7ELup`cC7vJXD0l9NBVsab)ocm)dRl` zgpI(9m1XNi)s;!E{Om22bRp+?Gj!uoS`6xlLVDCrr~S^Ok$uIM`rdfhT47a~t^bD1 zV+#ok@^bPD=PZnyGdE*wCz~L#+(#Y6STX|Ez6>rC2?z6Wmx9r@FVuoF?AKKVJD$wn z!OHG|Efes~o^m#0StWg%c8p@GhqMGEk{p6Y$I~v`_Mc|V=$@rBz6;lGNKqzXk-~&S zD!7**DGg1K;u>C#Jf^zM?{olTE0SJ2VdPZp@7f0qtGZ@Uea|)aIk^4Stv$*6P{#!N zT`7py;qLLlI||w9M|6sB88MPwYLYX8xBQQg7Lt0*87_aQU|JG)`)4>Oo_@c#o6oK| zTy3)So$pl_ep&CGQq^;I?k$#HGnY>3Wx5G%KI#WKEx9j=EhMsWM*{66v!!tNx94-7 z#`W6TRb}tM?J2r4r=0*zeSbeuT@GEQLwHTldzunxt*(_}Va665&cDglV>tyUyIVl3 zr~Vw_V0PH48k=qz>&zPH4&wL3FXq7Rz%*yoV2mgxW01tA<@>Q3jPzvnp7lde3+m=8 z$%bFGBaM)(xQkY@1e|cS5W&=;1xPt!ED3x}5?jUb1o&BxQyo}uiUR_D%!O*!HAOST z;__Pz(;>C|6b1AIFm5!{gyZJ;>tQXMZ1cN_kkEjh@k2#H%{m*i0*vnK0ZjzDz~|>B zr$kyIPp|rB<-E4JytS7$_K-Xc>#bXsj8C=%Lxo0L4~#BsX-|ZMVu%ez`7WpXIF&4u z_a@ty{Ej_eXwj({TuJUyfxMKV~KG#d=MDdI<%3KQ(X2rm8egudwU5=(c|N2;p} z1j6TybiE+&ilUlTiZAevpRn-MD!CXec0=x=&+-XNnoLuIi?U9p)f-}ghgI2 z(-@!v8RJ(=Fbr*0)~saTvC4tbgr%yidR}vGWzu<}U%uQMg(}6UEY73UkXu>oOUYL$ z4^7i>+CAKNA%SQ-lIj@16?j`Gc-G8goZB47{AmZ859CgIJ2S)Q!79ejhuT}M3sSD3 zXebd(&jDnXv>z(U*lPKxP0k86;c2u~eSUH<CZpLeyC3&P6hKEg5I?1|3zY7Lz0Wik+N$b^nm4-@PYBJEn35z`W7? z5eNte2;##Zv%`N}sK3t;Jc}viMP%06LVQdy)>2LmjBxEn-EJToOyv zPgyf~G>970cuuY!$7D;GCcz|Blk0cy9XLpdrP9kEZS*Y5Q3>rZVu0eOYE*~`Cm(wk znIWm{iQRHh>8AaOOK)zB9wA^w?}t>JzJSceaWp~Jc@@I^=wDU6-?#1vJKAs=C~#McX? zqsuv?hg9^!iaNZZrAzMMF7+hXP9QgxyQuZz(2(&krrj3G=TX+vGr+5pUk+(UO^+?P zrYY$f8)l<=B#Y0*o9z(%UuCY_HQ1Ep^eoqhs5ERM>3WusR%H8zUS$O zJ{J!kzu|L!<>Der=aWbU2{aqYXVs^y20NA)j}-LrQoW=sHygC08UGNHaJM{L;aq+U z&zZ=knZtzYdGv&yag2Z$8hkbW)9cB39=kP?B}tSGF->Y!HD?vR_Wr!079S-X(Yg)< zqQYM)NRgFA1PtPkjs)fyT11ezPAwIBA-vCBTF{Rno8afYrwwnMM1YV*l}p$onnhz* zV5@VT*d|Oo^#xKOy-LuDd5{m&4wUp%6XcN#P&eeWH!?FD7PHV?DcC^`GDMqsbVWCyq%|hIkV`zbagsbexol)RubB? zB+kuG0s+ORRL@19g^_}DH#iDvj^N5DVbQjcYF<@=8mP$cji+tY&3!Da!nk0Wt0JC- z&(vZ)-fVKt>xT-2m9VU|9#%~bA^ownu+LCZ{5PJcQ)M9!Sb=(@>t592!|l5H^tD$L z1vL|uhlgto*z4fR)@#7u4#z&=OGF9Wfaia5t~vsz!c8XQfm7XsP}jD!4R=yzN)(Ia z+)xhmFb2O&Oq~fmibER(m$9j<<;QEvvNPGBV4jFBD9g8;ut_mUOngKI|6N$Ic!>AFs5{$C|FKZKiDfSa0wbbT< z1>sr?B};+&3C&QoZzyNOp_U)*G7{(9#Xpc6m&R+VbL~GxS$)QNz`KT(DPza}DMS`+ zW2f-IHHm&Rsjze}F&;BiQkRCD=Ny^Dqx#y2p&lM~EE`|Eo0>=Udx!o&LvZ};=VLv@GEX>qA8L<5|eylvNgE$}QO zfkHM&&WY2@qG@{|bh=~#0py={iNT!TX*%B5KwWi&$VB)?NME>4*kTY4AI%eFjaYZ~ zO+%5`S#qg@1X$G7(ivQ-ZU^#?TCH=j1k>2?zg8=8M!r4hP_)vgZbI7ARMf$TynKhK z;2;UWC0iv!C)Y1@koCGGK1T+N#6M)S&mrf=%SGM<0&b25!5y6Sk-6OpJkkgb#;qZ4 zj(Hp1wf}m!GH*}Cr3Kw0*r|L!_r&ZQY_(FA&tu_*1w@M6JRL5Hc?=5MxVyiAMCMU< zV9nZ|5<+`eX?1MF>h8VTWg&N`wyv{X#EtV2Ovk@wP!D?D=~buIWP_Q^!_so|xYv5- z6P7D%D1^-3@_|V0tohCXvysR!DrMWMY124A9If1}f}`b-PVnBd)jP;0f}6~8BF;vn8(udFy#~@wD=hwe$S7{x@pu;edWv&4Uv8Y!c7)yX2q{A z7V4whHf}8xEuz^L zJG>&6pHwVuL2BneIPm0b-j2ynSf-S^yzMj$^WHv&7sqg~BM8=B6E1Z^Yn4h*H1JWl zEBrH}*61>A)~FMb>-8Rm^SwOl4=ao^0V#HOPA2 z#A1R3OKGZh(|AgO>>^6%Gx0bOO>C-I1_9cEy0C-FkC$`G6z%87vPkw)!-*q|)*;Oz z!_aAAU7+#`x4J~v{q#u~WPAL?kEeFpH@409g@lX6*czr@{qZ7K^qCV~fiOp3x{4kU z!Vd8Liqi&W7z4OeaqXiOXrM_~t!B%?h6vUQXI{W$mDQe96ruR!Z(6%sbkWQRj;)x& zD5vV(SD|j1k3)T9y-<4L74%ZltO#Z_0=)ghk4?WfYI^p^N=CXmU^f+@js|GpkUl zS~_>p9lJwC+=!4hy3g{fVe`KES0%=;W`oJYpQNtnW%79{7*L7yr|n1xSZ|Jv665XY zeYr;T+{rY3!a)suH`E5)7h#FWo7}t=%P(<>=bhKNM-MA&;(k~Lkr;@|{)jhBMmj#g z_EYw!Av*Lob%%i)_FMIvmgApM1WPlZnVU;9P=Pw@3B^97SPva&%`^xTh!vsZl^+@A z@;}~ZPM_vrni*G4KJ}#kmTf_DMgfb)aT$=Nd1TcJwtV4F zA0f|KWpohJ?>fG=w<1xa28d>Br+>-N5k=@ENQ!^3#T~5M#^B#!hcB+!PEKrvmK_GA?GW?swlpwl=qbG5^{v9WPY|N z%guo#!8;zK46l&<68(NK2u2IXu(MgG6@{f1jJl%3VHn1__onX}1tnN5B!SHa=&$?%j_pV$xS=U#nO$IL&3#x??Qbot@+~d;Zz@CDROp*z)N*_(V)Ymys8P z+^Z16UKX@i@2!Qrz}|`kfj7Ir&R@$i*NtzuVJ_x!?n0@%G0=;w!R}i>S{Sa$CpoiF z&~xh_%LX=_*VrD*O^w8Bi8l>Dm#5cQE*nCszqaT>#@+zCjL=4S5t5;LXnCDos*=oq zRL%vXO$+MogZy$JBn|6~$>WQ+6>9omWFgi+)jecrS=f4ApyEg432Ty;DhYNCeA)O& zupsk_-i%>I4Jmc-G!&|X)aekSn8WuKOHP(LZ?L0LE#4@B>{Ivw=>$*6)idY|I~#X* zvHyF@t~?L+w@X{JZ;y1ts#SjTqjVZ8<0hqcHBuz^J}xdZKG|#kZYs3cuE))gfPmau zfPmotbe$a>-7Jm&Fcdr5qxQ>eXsfcbFCe3($$`u6>h=n!XZAQAu_q~pQ( zVBokiU2XdckIwkZ&VT<#3a*n9Lx!)9^JB)JGX?MEWk&b?YWwt>sTJDn$j+C{tK&`< zUPZmZ&D$~liL!;{5dJbq0)$b=Y=M2} z#$>mcbc}c}Mv9Qc$ud0IqBG|<(Kax^>41S0Grha@)5yXJGioAxfpI44di42W2g~S~ zeep&4!v~Dn`##0A1tox~SM_u!;NXVT5fM_xRvZPRsCI8domo?#wBKcR>WJ70?0pv9 zg1Tyg;DV%jo3tW-8;ZIKYojT_=eY=d?Q5qDe*Nyqe?=$RKffcG(@|n`HEE1vTZ z`r<~9iJ~$sOn|8(kVqqO9@zciDn}=2#=o+(_u6G^I&IrtvbJ~rT&RaoxpB*;d?0nz zhG84_GI{J96~l<(pe+YN)@S5wbH|smmn(X7s%Wk-WfG*U2hDE054qRTJs@Msi2D!P zEw0GVnP#>Lm)M2i47A>tGsp0dVaRx+GVmG0T#x;fEhd#}2vbFOdUXaLUaf?4WyxV1 z=6#yuFU7Pkwz;-42WyK_1d_nebv;`Ue?+WD1aIu5iy?#-Iy8)Hk-X=A#HJuwnyw^6 z{Je&JCNQ*>i!VYTtWj#kY0{zS@`yr_d+BA&)La=Jc}|&o2)J#n43{~hB!s-CW;BrR z(gb?s8UxEZ>;2(Mh$XshX^Im;n{CHb;WzNZ6&OpD(2*%pWDrm(g~+CUa;&6={H&IH zXT9=j6@Yp6M$!%A;sdQ0)XKY&x_K6LZqoGJ+YG2bumV)6ZQF%aB;KdAs>iE)ED}W4 zl1&tS|3Q0aO0=bBzj6#F%jC{vryveaCv2n$Tg{vzyL0~v6(eK5mhr`4XuluYraRG1 zK=Igb<{&8OGRU0>f-OU^E5O7pr#d*K0J$X#GQ#snVd0FgUu-Ju>UQNT3#-u~Oqj@5 z!|Q}#89xDHQyy;}NK$<`p>`5)q-At!l8LQe?8K#I313&*E4sYMs5%c?pnnns=!OyMj_XD=?FrWj|#T;A#o zR=uyRx?zMRKpop_RT~Srf_HT^%R3aTfyYK03$ReiN=Y^L&K-x3?+fFsxXsfn)j}hcs~!cMzR_tk;80*T?nTcUoK%5q(Htb+c>_tu!3@J z`)?df$XiX~NW+U)IqYS{kVkKIAtIfd)C9Ae2@C#;uE zeJ7g}B+++*N)4EUQ6FdTn9NDtFl4`~2WisD3s*v9i#;he&h=ePZ6h28axWATP{05M z1adDP=oy!TneN%@7is4JP0FR=gB?qlNMm`ARBF%L zNVB08^MakuNtx1RuTBHbm_r1!z3^jYsi8ZDJk?|VjyTT7UJZRyP|UL>wAYs;h2wDM z>henYQe(jS(wN>|+M`2%RC!Okykzl>W6ZIN)tFt+hG;ocKXtrt_nFFScx~Eu_qZnO z!Z`G>$;Y7hKxAw9t#3I)h;Y|#GM;Yo@GX8>$DVMv?2AOwzW8MPv@AAx>&qswI8P&# z{#<^hd3Yh>dYPoK^46SXYk*R|W!whf6Rhgxgr{y98lUsiV>pi&@BWX%LDv{O&}|v_ z_%-kRZwK~OQ8z&$iMYs2#@@4MkO=q2q%EpDFT_z{ZqEaLitpcBbgm;6;}t4VzpVId zmLMA6IG^BJ-ypPaN8;tU08IJ-6H(vm|Ovrx?BN>|CAm$j0t&t_q5!e8zyJ5(9|H|NF3jhsQxB zVzpNa_WRG9L;T2>trsd@HsS4?bT@Z7xz}w`2z*lWNoa**K7&_Il!%&r*A76pds(#b zy0oPl#D3*l;4-RC%bBApowwUfg!tLB|9pMM>UngK$lH>)78q?&0OUE%N}Qya>?q!m zfdq3!qAgKb__2LLT=k~B@ZzVwAy0+@=h*<(dPUlAplg}M~1 z@X~G}M3g`<97;aN)6XJ|gqttK>lX(>sFi#Mphm&ep$%o20~iXh1d`=pIbHr!9qgNr zMSvj>t2b60BsZWW2I0fKv?NXGG)nSWaHwYyU_>ROOJf~YOp9ew0I%WU9?-#bged1}u7A;YlsyG= z40zyyh{UC$|GvwLskkJwb=^<#KS^F87ORGN?jK}{i|rUO{8xL9;y=la(zb}C)!&FF z%a8`(mF;r#WdMYjd$#F^0`@;hY@#{fdE)*g;UeGvqfv0T!@KYMl1VE2;Hl?eWJxzA|LcnN(_KcC>Y zg|3I1<$2*GhpR}86c+1x#7F0(j;2OEKQbvkGWhc-xm(Yc9!pEY3y*w=JFuvx-9%mB z8~*ZXwkks-`)0itZP`aC)R0e7md!K)3Ock5?4;s$7NzS~A9l07DAdwT?~j<8B~1_$npgWI$uF*o%@PuSN;e@Fr*7||Xu|M=Z6CKsO$dUycwt#k7nFL(RL#cozoLPya zQoZ;@t&|}l6FLEx7c@b<(&L7L6EmC?Bw~aQ*t0;Ddmo&VsU6~}rs$t5dX8CRdwv^IZ z82rk$d)JHv-z7lPQPIPtQJ_nH&CWnp;<`2Y&XP%9wuA;M1HWct>gd#%F=)T6h6&_c zDLJ^_eoz=e+1^% zjPUAa(Il|H+UK2qs>rh@f*4Ow2aDClUyG+b?Hq{)%N@%yOSsmeJS*-u4{Y3GK?G9-UIf&l5`$jw} zUgWLPqDZ>FXzny)3@_PgtyyMID%IXkQIRY%U&TOgz^BF=TX0t=u449P za94Mvzo$!_EEPI0mr^tvW~P6&{hl5zqJU;LfN;5J)>9)GULd7rsdotiNhyX?o(~XQV!AUhpPOEe%-NtMdk$HB>&wX`mo{>Wa+=0dyR9UDAHeo{*GhO-oliO%N`b4mdvXz% z&ihbMyXlo|HKBPv-P&$3W@iPgBU%T=8Wm$YqdpY9#%E|EjzjH%jfiEQcQa-4$cJgA zvqQ>hUQkc2qcyP`PF|#POF4~(Do9;b96IZfRR9fT01q0*a4B)aV7|Jbh<8>pLsPK9 zLg_CWWKp};stXNlfrvaP`BPt0_AOdaQb(!<9d^sw zK*p8N*zru!&#+rRnbe4?oOve|Bl^&a^LW}6Ozx$6q(_-;Y8|T_VOrc%ptg%@t@H2S z=pR^~at26_vxHYUV+iZcX}q(52E#Mj84_Xs@`6o;*$ zmCPtO6PC78?3eS5u&CP%wIearuM7riwVqGU-mxJ%ghfbG%~iJ}Qc~#@Fp!^D(1gu{ z5D}bTXodQCRfUs)I!m|9X3C6NaZVgE@p#*qH51kLehsAG?Guguc2XeRRNresWm#nM z19x$%&fgUg*8~%`$g`L@3x~lFGSI{P_}gZkvIdq^wg1B9iiaUwNgRZ3aqMH)XN^bl z*F&FwjjZ@MJ$}dwh|fs}Y+|GQxhv&jY@qnJJ?ymUaqAvB81Redymr9^&b+Fxa>APo z(o1g3AOU^(KT?`Pd!cN23qe#upjY$lc;9#z&E}SKEoy8ajDCDKU>O$%)5)#&)(xGE znje-hLP=mP;fK#>`w()vcd5UMihHv5S>!F2L4^xd=6gsS{yE@( zhO!8|0`f+TjE#PsqUR+p+b_`}1f6rvxLiEo?Fiy;d|;3k#Jh_Xq*??>Mg;Z4mQw#h+GwDeR=&+vgSH=eF&Kb(NLjxD8G8?NtC4Dmyt z9EK1-h7x1(M*^mDaCAeIDP-u%2UwEK=IZTCya9bv$4|966;KIm!&fnx0 zMEi5)B{!6A`QYRDRFtFYl0=>c_Y!Bsp`B-mPdKM8DW zqTXH+z1gMktZT8XJ2M6RZK!iM(pYsj6S-H9j|DfU8iQancTxkgmbA%~EmMTW&?Ah$HH#AL97Zt88>s^(>Rw}t2t&0ls$}(EDS|KO6=j&gz?C%eRolA2TVWquJC|Pp%Kbmjt*9FvJ5&KuC<2he|EuXN>ivS2Oi_ z{_v)6mL&> zpPIc);1BQ|?l2?{8iqE|5PEf(>;cGKu}St`33E3lhi9JnLBd`XGzCNqsuyAQ(fB?k zi{pFH5XR}Ofz44mEcv3U0k{VsJHPns?~$8PK~$Sq6;D}uz(Z$r7tckuOcZ`H%J4UI zkVeY4VxwkxK9)2oB2Z6xgJyYEvKrEaw~@%_pKO!m_0gjg?2Jxyo&0c$@3~UdZG&Ju z49OW)N411t!RRk}_+#qLA-RZhA)}%t1!kg|P?|xTk?gE^El$&KnrbS)&G{Adu4uSe zh|!YF7E~(>8mK0p6HjP6kmrJ-N?YONPeG0@oplvXpBPN@O=N;@jLqz|6I_EQF1>3k zcSs0=;}CRC;L1`0B=EK0*OEiXf?lX+od=tEC1YdaGH+_-wZx;3+)2SKA_*50)%8X}Ad0ob1+EV7xMuG9gDS=Xcq7(gKtY zsOv*5i`^PD+8992=J7aK;}zY>Gk*!-FhBsi&U(F0PVj z?&R|}-ZlBa-t*t!cCCWl(R#P?{;N6EEZZJm0SMv>G!PKraO6J$MJ9m!IVF7qOXFX5 za3x{gdWjBY;GFanH)o~s5n5W_Y*t~4qCA~xDNH-155b}ux4$6%%YyN`p-ole;+o;0 zGZI-Hi0mm1nvg-{Wz;pU&tO^__f${wU;=*H?dryq3+|Xg1|rLGt4m|9f1gu;5S1Y4 zm!@-?YTHSf^-fOpn}Rg)%`SHmcc#9HufSB^6DaqjJ^o4~Qt2@w-B(@ngc@Z)xSV`Vpa%sH-H|arVc=CN9K=ip z4;ye|%kAE1l9~Bf8U+I6uuE{DYofBRX(RS7b`oT6u;I}j2B5cK z6h*Tl*woa>GXBA8O4D4gdBamj3>9bebEwfOHQl-xeCvkw zUv**HWPHmPV6Kk<`hW_s9yHJo3 z#%@JgFG#nbDE;7-qig6Un^A2f1M9DDR+?Fo0#sdglo9ag=G}1uO@hN|nSpWR#KFtj zZ&|oW#y3!N<_Vjf0WE_)8gs0ZW}VrNZVfI~xF3X2SY$Ob;f6Hdtreu?GY~k!&5%fY z0Z}@93IaSwA7r1R{ARuVd{E=~rGn_2$sxqS{T2KD`L-p7e;T0bkf9^Uo?!!ae`|jn zy6pYC2{+?y1pk%(JPHt7gujcezOC&q1OC5K3lLerm8LK%1IYbtBfNx{KP#UBMitgn zErPJyAZ8A%HMT*59&E7mU+VlKXbQ0j_Hgv<-E*S<9?`czqDUvrJFJK|5u4M*It798+Woyw%R zs{n1Om+dH?q|i9@g>eh~<7CdjP{=W~s}sMlxA%v#O1+t2e50I?k1w8(mI0ef9=Z8q z@zg$33{3|m@!VdR6D@9f+k||Qmhv6o>2Pg8EJ3!F)T_MUbLd7?!5k3lo~_sjmicr| z*|%=QQbKyXA1$?x1BuyLl=AM;TsjyRqoF{QOE?T{QHr zr;oo8=`Bu|&NIG*HG4U>E`q)x`OflaNl$(7EO9dQW34xLGZQf6|8aaj08;~;+<(v3 z|KpeW8~K;qeL2a0GWciu@ZYdNRe(kG-;#-c2mYQ{`4@Bw5K{d=vMhgx|1Tv-mx3?k^V4 zfJN~iEdCxv_dEReSen1!BzS+q|8Hc?@8I9JJpKZ=(ft?v+Xl$*9DZNJ|HXlt;lCXI z9KXM>=znMP`y%WwHnmK@vH9~{{od(VGy-~zZ9e+=V4eCglu|MWQiLIVMbaR34R p5C7wL_&+WG-{J3Ee}n&b9*~m+1&BEi5FFqy1fVHpzorwQ{{x#L8V>*f literal 5710 zcmaJ_1yq#X)}{xM7*asGL|RH(Kp0BWQ5qyA24<*_5)n{BQc_w{K)M?~kP;YXXb>co zkgk!w1OIog=zp(!)|xf5)_LCZo@ejNOvGPAX&PM4H9OU3&)PSe$>x4we!((jCn+I1T?U{xI}pWGGoqAF*5$zg4hYgAGp z#z(2)X^DK2<(cnvXeS&;=wWPg>YX7*2;X`rtTZ53?76KkrdM4P$C+aYazD2fhCb_U z?1CUo=0p24+b3@BrE94H@Rt#otP|+-N@HPQX#Gz{NYN`Sp%z-sP?!t91=QJ!&%*)Y zqhqYuCP3kX(&h1)sV4>+SHI!hVG9YN4u@7Xg{_W?IGAm1u8WFlg+NhLE?yhDF%!)^ zjr2CGT!}1XFT1gOxcDs7G80DHTIABoz|IC>G96cp0PBfIZQwpGBF%c8cSRTr;;gpj zxjL0)k2|PuR{KRKwcgHV9gE}3chg$Z(>_piY1O7SSs>&2I;=BAePjnyP*Q{Asa1_A z+PBgKVyQ-rJ|P1>8#F-V)cts81&_@s1>oMukrvNGfe72`ivzrdRgKSaT6h`LKWqCi zCqKF)tM3e9v!>0%-4G9`RXs^^7oi1hdWuxQE>#K2`qXGDyAyfmwE}#5q8(fu4ug|&zmYkT!+`$Yk=aItu zD$6k|T+XcO3$SORI#9&p(NgmMnO?`qOl(NK=;uhyC)GD?G5D(6s1P~FTLZp*&6pRS z+9qM+v&Y21`20st0k3+>3)T~yX9=DwzRJ3a^0T6)t4E#r@>6meA=baG;KR7DkhKp37@a%wM1J@h9s zkb4E6MZ|xJN*%!~Oqk7-)hd>6LRkltEl7v;@dgz657|iyGG?!rIvV!OY44Vh#&)UD z-JsTBh{Y%qAO{+UBxjxTgKeJIrF)kQaBOkrn+ap@%3eF;wqKvn>Ls-yos3~ZbrMDk zF+P7O)7#nB3&nva*oyV6n{Z{cOwVQU5GzvkgNWxy(_5uAI+EhlM=hh?vva!yZ+^-q z#nn`Z?jilJf5{|FtU02Occ`-^|Nlt` zI$hixtQ=i09Pp2f^cJ+q5(c7VI-;6Vz2u04G^T@RAFpDE4ej}-UKi5dTc_W4<_bMFfq~V#cuH27)HaMXFvHmoc2T3Nl(5uCaBzK~(aU)>`<#2<$#} zW&j5m1b&ndE_Gec<@T-C*P-p_9-j;BD2odv z?1cB2{Q>o^)R|d(BGE6rQxzCz(-+>~@8tdfEn;YWco-;flPse=kf3gJD{_wAC*UoK z7arT|&Eb0=?|U+Z37#F89#UTLBd~uwJBmJi{2%$b(2z@ht}@f{_;bW(QJ~MJE>ND) zfXQCpM`DBrIgU-J;=0sLVUcM3&>K;#_S2c?Uo!Zb*YHAOe3nD4%mSan6Wf{h z52mvU#)(rBKm(bU!V%OqW`g;l`g@-HbE~4}40Ui1E8)IO=^x9}C+}<2lW4TL5#9K5 z$HRS@lPklaWE3b}Rf4CDJ=?0(aY<4MSVv03C9sM-g-TmGR4Ay^KUdE&WnQ_YIJkj5 zwE3|VO(fJDK&dqUUCs9NMfI`4X=*`W){5NrB3ED|k6wj=?4@7&OyF^=d16o4nDghnEidIw8ZyHMbf&i7X-S(UgPz zO$qBBWOyOlhT)u&PS2iIVcNl3*H+t^Hp?m4X5mL}rW_L!)wrTd(yQ>EgE`yJ>3}hr z_}Y>eA}bNP70Y*N?`TUTT2KV#)ffSJ37{_|gw}8b8f)lWRh*-7ivn8y6L5>R8diPgbS;)_RABzSA}$#w95k9*>e~&fC3r9`J2fYdK*S zRdSx@b|RU2V!EC=#AGDlOR6F^(|fO!o#_6S@`e)~;Z38Ru!y(3o?wjlSv}S=OP_6W z-Ba#S*={c7O^u_oMoSLg@>>!ZXSl&ga()fU=bnp!gx!B5gD_KapVee7@p%EqYo0<$ zi0z!W{B9teN;`I-uMs(^Z0>UgHiOY;GgBrr#MgXbt9k}<5zH%Iw9Y9!pJK+yJg5kv z{Yn;*Z>FfF6h!fQ;w?(^%191!&I(5gvXZz7k6wZsS2O1fykU3%dhwEH{8%c3@)FCS z@%lGep@w8O&(u9>23lH@ z8#>QURFlsE7ddG@{@VQ!HU`G(wLjhKB>&Dy7gtY+mCJ=zH6?s@{455Pok4NqG)tDJ z_R@mlTHS84$*xj;&u-ngsi;kLmm)tJY_w!}RO>s)_+2X=(i;oxa2mseIC&uZ60hId zEN*^3<(;W2bQ?=pY=t$FfQ6NV3e&5$Wg|T5JrNb@Hys2qUHWCc&v8#p?ZoG}{8YQlddXeryJYrjCO`MYM|fgFgdxbg)$LigVR&*A}T~yt%QwyuhWq&KxOMx@Vc6Y}*jvWjU zqlrrhJCA&z1NB#++zGVKfXuy|&y5LkmtcuHgx00KTd|y8ZR^ zn8d8WJ~}sS5E&Cl7D-#G!f@}F)Q#(>4`__++y1uJJvk|9?qnX;X_0Bi6*tYvSd@(W zz&AA-Q9m_WMBk%r-d4806bTzx_tq}+eu#fG4c_tgcn+p%g>XaiqMs}Z*M=|c-Ek#C!V^Yr2iFS9%?D6+FCl#*lLkGFD*2@z9aX^Hmit!zoM{MnpWdAn9y=roQP2>5*4Bo zT?AeE{ked@ynHR74i{C)WvY*W)m@SWfK4b}0Xm5uOl-$VR~uMoOm-2;;aiF3z3LZ) zNy-L_WVt;hC6@#xY+2L}#e#~OGI4Q_GUTzuZVasC{q^F|T@{1v(jLy1iYt1-IE=e&X zy{4Xd!zE#FRH%zn@Hj(CtHrs9JI@LPA;x@nN>cU2!ZP_YSO~+yb#=k3BA@4ZPjR1q z_vvRYAS#gO>I)gI2BuWi)8#GO%rC!qNw2xYOrp(4A#51Uk6i}|zX@tmns{}m)U2?e z#xDz+31=;mDp$&w0|lW^d7cIw&DXeOJWat3BFA@S z#_maJa^%S3(o_UI36Z*ydT+ANe~hhC*+=reI#yLaqWZ@J%SFtW0t%iBlkc26vBGjn z*WSy~;cl;-3TVi5^zvDdkV8t}QVf0wMUtDu#5FTDRgTVcMR171UW zy$PEXVO@AzFLuY4moDCL#~#QoaL+Y1v4g5r$Jf!VhoAu=&AHz0<^GN7_({KCS*R`q zgaiy)aGF#-aiFI3p)!{9Q;+o9B6Nc~8gbQ2bv7AAWf&~H@$Td%y;kQ?0w2yywWBKn zh9uQX+CR;DZi3x@H%i?z=oate5==AbSE}-aS{fmPzsP88=2a^+yk=hg&9UQ;lc3B0 z`D+~kpCer?vh4t@7|;{$LfD`ooaJxVGrK}e5ldhxlE_GtqM`zJ{2GY^bCzO?tm6HO z?CPTJuSu^N(`{*7Sv+#|@*a}!>WUbu?CX8hJpIFvw`pX#IDa!ko>)4_dWjTO!NKxY z-PC$o&S7+Y^YQ4pVVIe_w1e+=r0K<7=0XkZ%|_Rj$ACYDIN87NGHa-#E7;5&a@nLd zJw&Qv-vK^Dam#;kfXf8aHBrgo$Z>nD10X|9CQK7_6XO-W(4=ZDJ6zIr!}1DAAxXBZ zo}dczaKG^Ps$U2RZbwy`(i%K+mhohDa$=*{0O4aZhxP z$ElV%A~8vx1;v(j0swc6+g0~vcp7F!1aJNIbflEiF%J8_vnKg-`Gw?8n`c``)65<8 zDzew_IJ6e6=tZD56imr#ExoMi%HNvve`&=2L{$H(wRT-UpvY2NIc}+yra>Hh-*G_p zX{IB|z6cKX^-x3Q^^eWLqc>}|4hl!NN~$&<_Il=5*gHYr`rh7$P*FRNXLdeGDkKoq zAorRT?>97N+Zx+@93g*iJC>EqMR3I_%OjuPa#f6v>1=f3)!K0F4(B(`^F7=H0D~{q z1?zXs`l&_HU5_5lpIHA-RqtZu>T2uw^io(9H5(iQWPxkDl*>myfYz{M4g&tl9^&1LA!_tK0j2;YUg?U@|{Q;1}T;4QGT7BK4}rCfnp%;Dzl*3 zf+Ob!;>(B{d!8NN?Lxn71KPX!-#&S-a6Dj=ge&I~N!`-Q+Fibpb}Z!*#6qQx-0a;! z-ojrr_;6ya(V=6NBFR=Iemr<+n$d;Hr>zM)(}G*q$wQ_d9+rQCX+LlHdhOcqbI1{f zHq6a6MbO6Z*tZsJC%N67y(zmu<%|bZFlBH31kWG|JTdk1o<6(O+%C4bZ&T=SlR(q+ zzcrT}y~6N5IvCMzE;|dq8;ic=9mI z*_*N0gLVBHW_kSEIh95%`e}*K(P|xD(cFPipq8XzMQ^4hjowOZwQ+x1((y?k@3)GA z39r_$N-F#6*T@;?2xnZ-lQ}89Nn2w9mO(++sq2Sa60^Eip8Yu$q>w&~PfvDN-@xR! zyW?3t0?gi7pNPvwHY%IOPp=b&=nKvHl;i{tN0t;FfqDTt&KttDgADx2tv?3XzAg%i zMS{6y-@H`Xj+R7G;4s^Xg()Lj0QJ=)WSk5AQ8_P}QzCpP)==3rg;(dS)qn+Vek z-z3Q#WH%Xb#dZ*9A%b*L;{YAkn3^-@>5?G{aJF+Af@d6>k8Q3^1}B^a-@P|t7M9kG z7yL$QsWzC|Gf&{dkBtP8^Gnu4{>md0K{Gyg8IYJ*6c|6pcUQ+)7vsBM?aDAuOYK+S z)v3xwEdMkz^j80G=Jh-L>S*9%^zzdh(Sr3K_&dPZLF-@lV5q-=B1~ z-2e3$k8%G?+x Date: Thu, 9 May 2024 09:09:59 +0800 Subject: [PATCH 116/134] Refactor the Placeholders and ImageResolver classes The code has been updated for better readability and structure. In the Placeholders class, comments were reformatted, and the findVariables() method was moved for better code organization. The ImageResolver class has been modified to simplify code structures and improve exception handling. --- .../verron/docxstamper/core/Placeholders.java | 22 +++++---- .../preset/resolver/ImageResolver.java | 48 +++++++++---------- 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/src/main/java/pro/verron/docxstamper/core/Placeholders.java b/src/main/java/pro/verron/docxstamper/core/Placeholders.java index be634790..c22d6672 100644 --- a/src/main/java/pro/verron/docxstamper/core/Placeholders.java +++ b/src/main/java/pro/verron/docxstamper/core/Placeholders.java @@ -11,9 +11,12 @@ /** * The Expressions class provides utility methods for finding variables and processors in a given text. - * It contains multiple constant variables for different types of expressions, such as VAR_MATCHER for variable expressions and PROC_MATCHER for processor expressions. - * The findVariables() method uses VAR_FINDER to find variable expressions in a given text and returns a list of found expressions. - * The findProcessors() method uses PROC_FINDER to find processor expressions in a given text and returns a list of found expressions. + * It contains multiple constant variables for different types of expressions, such as VAR_MATCHER for variable + * expressions and PROC_MATCHER for processor expressions. + * The findVariables() method uses VAR_FINDER to find variable expressions in a given text and returns a list of found + * expressions. + * The findProcessors() method uses PROC_FINDER to find processor expressions in a given text and returns a list of + * found expressions. * The raw() method creates a new Expression object using the RAW_MATCHER and a specified text. */ public class Placeholders { @@ -62,28 +65,29 @@ public class Placeholders { private static final Matcher RAW_MATCHER = new Matcher("", ""); private Placeholders() { - throw new DocxStamperException( - "Utility classes should not be instantiated!"); + throw new DocxStamperException("Utility classes should not be instantiated!"); + } + + public static List findVariables(Paragraph paragraph) { + return findVariables(paragraph.asString()); } /** * Finds variable expressions in a given text. * * @param text the text to search for variable expressions + * * @return a list of found variable expressions as {@link StandardPlaceholder} objects */ public static List findVariables(String text) { return VAR_FINDER.find(text); } - public static List findVariables(Paragraph paragraph) { - return findVariables(paragraph.asString()); - } - /** * Finds processors expressions in a given text. * * @param text the text to search for processor expressions + * * @return a list of found processor expressions as {@link StandardPlaceholder} * objects */ diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java b/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java index 7b4c3d1b..47b0732c 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java +++ b/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java @@ -6,6 +6,7 @@ import org.wickedsource.docxstamper.util.RunUtil; import pro.verron.docxstamper.api.Image; import pro.verron.docxstamper.api.ObjectResolver; +import pro.verron.docxstamper.api.OfficeStamperException; import static org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage.createImagePart; @@ -22,28 +23,9 @@ public class ImageResolver implements ObjectResolver { - /** - * Resolves an image and adds it to a {@link WordprocessingMLPackage} - * document. - * - * @param document The WordprocessingMLPackage document - * @param image The image to be resolved and added - * @return The run containing the added image - * @throws DocxStamperException If an error occurs while adding the image to the document - */ - public R resolve(WordprocessingMLPackage document, Image image) { - try { - // TODO_LATER: adding the same image twice will put the image twice into the docx-zip file. make the second - // addition of the same image a reference instead. - return RunUtil.createRunWithImage( - image.getMaxWidth(), - createImagePart(document, image.getImageBytes()) - ); - } catch (Exception e) { - throw new DocxStamperException( - "Error while adding image to document!", - e); - } + @Override + public boolean canResolve(Object object) { + return object instanceof Image; } @Override @@ -58,8 +40,24 @@ public R resolve( throw new DocxStamperException(message); } - @Override - public boolean canResolve(Object object) { - return object instanceof Image; + /** + * Resolves an image and adds it to a {@link WordprocessingMLPackage} + * document. + * + * @param document The WordprocessingMLPackage document + * @param image The image to be resolved and added + * + * @return The run containing the added image + * + * @throws OfficeStamperException If an error occurs while adding the image to the document + */ + public R resolve(WordprocessingMLPackage document, Image image) { + try { + // TODO_LATER: adding the same image twice will put the image twice into the docx-zip file. make the second + // addition of the same image a reference instead. + return RunUtil.createRunWithImage(image.getMaxWidth(), createImagePart(document, image.getImageBytes())); + } catch (Exception e) { + throw new OfficeStamperException("Error while adding image to document!", e); + } } } From eb29ebafc3e615a4aae6ebf9bd4dc4dc0e23fbd2 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 12 May 2024 22:49:12 +0800 Subject: [PATCH 117/134] Rename package from 'docxstamper' to 'officestamper' The package has been renamed from 'docxstamper' to 'officestamper'. This includes not only the renaming of the directories but also the update of package names in the source code files. Additionally updated the module-access configuration in the module-info file to reflect the new package structure. --- src/main/java/module-info.java | 32 +++++++++---------- .../wickedsource/docxstamper/DocxStamper.java | 12 +++---- .../docxstamper/DocxStamperConfiguration.java | 12 +++---- .../docxstamper/api/DocxStamperException.java | 2 +- .../api/EvaluationContextConfigurer.java | 4 +-- .../api/UnresolvedExpressionException.java | 2 +- .../commentprocessor/ICommentProcessor.java | 2 +- .../api/preprocessor/PreProcessor.java | 4 +-- .../api/typeresolver/ITypeResolver.java | 4 +-- .../docxstamper/el/ExpressionResolver.java | 2 +- .../preprocessor/MergeSameStyleRuns.java | 2 +- .../processor/BaseCommentProcessor.java | 4 +-- .../processor/CommentProcessingException.java | 2 +- .../processor/CommentProcessorRegistry.java | 14 ++++---- .../displayif/DisplayIfProcessor.java | 6 ++-- .../repeat/ParagraphRepeatProcessor.java | 13 ++++---- .../ParagraphResolverDocumentWalker.java | 4 +-- .../repeat/RepeatDocPartProcessor.java | 6 ++-- .../processor/repeat/RepeatProcessor.java | 10 +++--- .../ReplaceWithProcessor.java | 10 ++++-- .../processor/table/ITableResolver.java | 2 +- .../processor/table/StampTable.java | 4 +-- .../processor/table/TableResolver.java | 10 +++--- .../replace/PlaceholderReplacer.java | 8 ++--- .../typeresolver/LegacyFallbackResolver.java | 4 +-- .../replace/typeresolver/TypeResolver.java | 4 +-- .../replace/typeresolver/image/Image.java | 4 +-- .../docxstamper/util/CommentUtil.java | 4 +-- .../docxstamper/util/CommentWrapper.java | 12 +++++-- .../docxstamper/util/DocumentUtil.java | 20 +++++++++++- .../docxstamper/util/RunUtil.java | 2 +- .../StamperFactory.java | 7 ++-- .../api/AbstractCommentProcessor.java | 2 +- .../api/Comment.java | 4 +-- .../api/CommentProcessor.java | 2 +- .../api/EvaluationContextConfigurer.java | 2 +- .../api/ITypeResolver.java | 2 +- .../api/Image.java | 2 +- .../api/ObjectResolver.java | 2 +- .../api/OfficeStamper.java | 2 +- .../api/OfficeStamperConfiguration.java | 2 +- .../api/OfficeStamperException.java | 2 +- .../api/Paragraph.java | 2 +- .../api/ParagraphPlaceholderReplacer.java | 2 +- .../api/Placeholder.java | 2 +- .../api/PreProcessor.java | 2 +- .../api/StampTable.java | 2 +- .../api/StreamStamper.java | 2 +- .../api/StringResolver.java | 2 +- .../core/CommentUtil.java | 8 ++--- .../core/ObjectResolverRegistry.java | 6 ++-- .../core/PlaceholderReplacer.java | 10 +++--- .../core/Placeholders.java | 10 +++--- .../core/StandardComment.java | 6 ++-- .../core/StandardParagraph.java | 6 ++-- .../core/StandardPlaceholder.java | 6 ++-- .../core/expression/Matcher.java | 2 +- .../core/expression/PlaceholderFinder.java | 6 ++-- .../core/expression/package-info.java | 2 +- .../experimental/ExcelParagraph.java | 2 +- .../experimental/ExcelStamper.java | 6 ++-- .../experimental/ExcelVisitor.java | 4 +-- .../experimental/PowerpointParagraph.java | 4 +-- .../experimental/PowerpointStamper.java | 6 ++-- .../experimental/PowerpointVisitor.java | 2 +- .../preset/CommentProcessorFactory.java | 12 +++---- .../preset/EvaluationContextConfigurers.java | 4 +-- .../preset/ExperimentalStampers.java | 4 +-- .../preset/OfficeStamperConfigurations.java | 4 +-- .../preset/OfficeStampers.java | 6 ++-- .../preset/Resolvers.java | 8 ++--- .../preset/package-info.java | 2 +- .../preset/resolver/DateResolver.java | 6 ++-- .../preset/resolver/ImageResolver.java | 8 ++--- .../preset/resolver/LocalDateResolver.java | 4 +-- .../resolver/LocalDateTimeResolver.java | 4 +-- .../preset/resolver/LocalTimeResolver.java | 4 +-- .../preset/resolver/Null2DefaultResolver.java | 4 +-- .../resolver/Null2PlaceholderResolver.java | 6 ++-- .../preset/resolver/Resolvers.java | 6 ++-- .../preset/resolver/StringResolver.java | 6 ++-- .../preset/resolver/ToStringResolver.java | 4 +-- src/test/java/module-info.java | 4 ++- .../test/BasicExcelTest.java | 10 +++--- .../test/BasicPowerpointTest.java | 8 ++--- .../test/BasicWordTest.java | 4 +-- .../test/Contexts.java | 6 ++-- .../test/CustomCommentProcessor.java | 10 +++--- .../test/CustomTypeResolver.java | 4 +-- .../test/DefaultTests.java | 14 ++++---- .../test/DocxCollector.java | 2 +- .../test/FailOnUnresolvedPlaceholderTest.java | 8 ++--- .../test/Functions.java | 2 +- .../test/ICustomCommentProcessor.java | 4 +-- .../test/IOStreams.java | 2 +- .../test/MultiSectionTest.java | 6 ++-- .../test/MultiStampTest.java | 8 ++--- .../test/NullPointerResolutionTest.java | 8 ++--- .../test/NullishContext.java | 2 +- ...olderReplacementInHeaderAndFooterTest.java | 6 ++-- ...PlaceholderReplacementInTextBoxesTest.java | 6 ++-- .../test/RepeatDocPartBadPlaceholderTest.java | 12 +++---- .../test/RunCollector.java | 2 +- .../test/SimpleGetter.java | 2 +- .../test/SpelInjectionTest.java | 8 ++--- .../test/StampTableTest.java | 6 ++-- .../test/Stringifier.java | 4 +-- .../test/SubContext.java | 2 +- .../test/TestDocxStamper.java | 8 ++--- .../test/ThrowingSupplier.java | 2 +- 110 files changed, 316 insertions(+), 286 deletions(-) rename src/main/java/pro/verron/{docxstamper => officestamper}/StamperFactory.java (64%) rename src/main/java/pro/verron/{docxstamper => officestamper}/api/AbstractCommentProcessor.java (98%) rename src/main/java/pro/verron/{docxstamper => officestamper}/api/Comment.java (98%) rename src/main/java/pro/verron/{docxstamper => officestamper}/api/CommentProcessor.java (98%) rename src/main/java/pro/verron/{docxstamper => officestamper}/api/EvaluationContextConfigurer.java (89%) rename src/main/java/pro/verron/{docxstamper => officestamper}/api/ITypeResolver.java (98%) rename src/main/java/pro/verron/{docxstamper => officestamper}/api/Image.java (98%) rename src/main/java/pro/verron/{docxstamper => officestamper}/api/ObjectResolver.java (98%) rename src/main/java/pro/verron/{docxstamper => officestamper}/api/OfficeStamper.java (95%) rename src/main/java/pro/verron/{docxstamper => officestamper}/api/OfficeStamperConfiguration.java (99%) rename src/main/java/pro/verron/{docxstamper => officestamper}/api/OfficeStamperException.java (97%) rename src/main/java/pro/verron/{docxstamper => officestamper}/api/Paragraph.java (96%) rename src/main/java/pro/verron/{docxstamper => officestamper}/api/ParagraphPlaceholderReplacer.java (95%) rename src/main/java/pro/verron/{docxstamper => officestamper}/api/Placeholder.java (92%) rename src/main/java/pro/verron/{docxstamper => officestamper}/api/PreProcessor.java (91%) rename src/main/java/pro/verron/{docxstamper => officestamper}/api/StampTable.java (97%) rename src/main/java/pro/verron/{docxstamper => officestamper}/api/StreamStamper.java (98%) rename src/main/java/pro/verron/{docxstamper => officestamper}/api/StringResolver.java (98%) rename src/main/java/pro/verron/{docxstamper => officestamper}/core/CommentUtil.java (98%) rename src/main/java/pro/verron/{docxstamper => officestamper}/core/ObjectResolverRegistry.java (92%) rename src/main/java/pro/verron/{docxstamper => officestamper}/core/PlaceholderReplacer.java (96%) rename src/main/java/pro/verron/{docxstamper => officestamper}/core/Placeholders.java (93%) rename src/main/java/pro/verron/{docxstamper => officestamper}/core/StandardComment.java (97%) rename src/main/java/pro/verron/{docxstamper => officestamper}/core/StandardParagraph.java (98%) rename src/main/java/pro/verron/{docxstamper => officestamper}/core/StandardPlaceholder.java (78%) rename src/main/java/pro/verron/{docxstamper => officestamper}/core/expression/Matcher.java (96%) rename src/main/java/pro/verron/{docxstamper => officestamper}/core/expression/PlaceholderFinder.java (88%) rename src/main/java/pro/verron/{docxstamper => officestamper}/core/expression/package-info.java (53%) rename src/main/java/pro/verron/{docxstamper => officestamper}/preset/CommentProcessorFactory.java (90%) rename src/main/java/pro/verron/{docxstamper => officestamper}/preset/EvaluationContextConfigurers.java (69%) rename src/main/java/pro/verron/{docxstamper => officestamper}/preset/ExperimentalStampers.java (92%) rename src/main/java/pro/verron/{docxstamper => officestamper}/preset/OfficeStamperConfigurations.java (91%) rename src/main/java/pro/verron/{docxstamper => officestamper}/preset/OfficeStampers.java (89%) rename src/main/java/pro/verron/{docxstamper => officestamper}/preset/Resolvers.java (96%) rename src/main/java/pro/verron/{docxstamper => officestamper}/preset/package-info.java (85%) rename src/main/java/pro/verron/{docxstamper => officestamper}/preset/resolver/DateResolver.java (89%) rename src/main/java/pro/verron/{docxstamper => officestamper}/preset/resolver/ImageResolver.java (91%) rename src/main/java/pro/verron/{docxstamper => officestamper}/preset/resolver/LocalDateResolver.java (89%) rename src/main/java/pro/verron/{docxstamper => officestamper}/preset/resolver/LocalDateTimeResolver.java (90%) rename src/main/java/pro/verron/{docxstamper => officestamper}/preset/resolver/LocalTimeResolver.java (90%) rename src/main/java/pro/verron/{docxstamper => officestamper}/preset/resolver/Null2DefaultResolver.java (92%) rename src/main/java/pro/verron/{docxstamper => officestamper}/preset/resolver/Null2PlaceholderResolver.java (88%) rename src/main/java/pro/verron/{docxstamper => officestamper}/preset/resolver/Resolvers.java (61%) rename src/main/java/pro/verron/{docxstamper => officestamper}/preset/resolver/StringResolver.java (73%) rename src/main/java/pro/verron/{docxstamper => officestamper}/preset/resolver/ToStringResolver.java (89%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/BasicExcelTest.java (81%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/BasicPowerpointTest.java (83%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/BasicWordTest.java (85%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/Contexts.java (99%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/CustomCommentProcessor.java (90%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/CustomTypeResolver.java (87%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/DefaultTests.java (99%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/DocxCollector.java (96%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/FailOnUnresolvedPlaceholderTest.java (85%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/Functions.java (95%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/ICustomCommentProcessor.java (74%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/IOStreams.java (98%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/MultiSectionTest.java (83%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/MultiStampTest.java (91%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/NullPointerResolutionTest.java (81%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/NullishContext.java (98%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/PlaceholderReplacementInHeaderAndFooterTest.java (92%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/PlaceholderReplacementInTextBoxesTest.java (84%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/RepeatDocPartBadPlaceholderTest.java (85%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/RunCollector.java (94%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/SimpleGetter.java (97%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/SpelInjectionTest.java (78%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/StampTableTest.java (92%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/Stringifier.java (99%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/SubContext.java (98%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/TestDocxStamper.java (96%) rename src/test/java/pro/verron/{docxstamper => officestamper}/test/ThrowingSupplier.java (93%) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 3aaf4056..46282cad 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -13,19 +13,19 @@ * - jakarta.xml.bind *

* The module opens the following packages for reflection and runtime access: - * - pro.verron.docxstamper.api - * - pro.verron.docxstamper.preset + * - pro.verron.officestamper.api + * - pro.verron.officestamper.preset *

* The module exports the following packages for use by other modules: - * - pro.verron.docxstamper.api - * - pro.verron.docxstamper.preset + * - pro.verron.officestamper.api + * - pro.verron.officestamper.preset *

- * Additionally, it opens the "pro.verron.docxstamper.core" package to the "pro.verron.officestamper.test" module, + * Additionally, it opens the "pro.verron.officestamper.core" package to the "pro.verron.officestamper.test" module, * and exports it for use by the same module. *

* WARNING: The module also opens and exports packages that should be innacessible in the next version. * These packages are: - * - pro.verron.docxstamper + * - pro.verron.officestamper * - org.wickedsource.docxstamper * - org.wickedsource.docxstamper.api * - org.wickedsource.docxstamper.api.commentprocessor @@ -44,18 +44,20 @@ requires static org.slf4j; requires static jakarta.xml.bind; - opens pro.verron.docxstamper.api; - opens pro.verron.docxstamper.preset; + opens pro.verron.officestamper.api; + opens pro.verron.officestamper.preset; - exports pro.verron.docxstamper.api; - exports pro.verron.docxstamper.preset; + exports pro.verron.officestamper.api; + exports pro.verron.officestamper.preset; - opens pro.verron.docxstamper.core to pro.verron.officestamper.test; - exports pro.verron.docxstamper.core to pro.verron.officestamper.test; + opens pro.verron.officestamper.core to pro.verron.officestamper.test; + opens pro.verron.officestamper.experimental to pro.verron.officestamper.test; + exports pro.verron.officestamper.experimental to pro.verron.officestamper.test; + exports pro.verron.officestamper.core to pro.verron.officestamper.test; // TODO_LATER: remove all the following exports in next version - opens pro.verron.docxstamper; - exports pro.verron.docxstamper; + opens pro.verron.officestamper; + exports pro.verron.officestamper; exports org.wickedsource.docxstamper; exports org.wickedsource.docxstamper.api; exports org.wickedsource.docxstamper.api.commentprocessor; @@ -63,6 +65,4 @@ exports org.wickedsource.docxstamper.util; exports org.wickedsource.docxstamper.processor; exports org.wickedsource.docxstamper.processor.table; - exports pro.verron.officestamper.experimental to pro.verron.officestamper.test; - opens pro.verron.officestamper.experimental to pro.verron.officestamper.test; } diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java index 6e6f499a..f5eb018e 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamper.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamper.java @@ -10,12 +10,12 @@ import org.wickedsource.docxstamper.el.ExpressionResolver; import org.wickedsource.docxstamper.el.StandardMethodResolver; import org.wickedsource.docxstamper.processor.CommentProcessorRegistry; -import pro.verron.docxstamper.api.*; -import pro.verron.docxstamper.core.ObjectResolverRegistry; -import pro.verron.docxstamper.core.PlaceholderReplacer; -import pro.verron.docxstamper.core.Placeholders; -import pro.verron.docxstamper.preset.OfficeStamperConfigurations; -import pro.verron.docxstamper.preset.OfficeStampers; +import pro.verron.officestamper.api.*; +import pro.verron.officestamper.core.ObjectResolverRegistry; +import pro.verron.officestamper.core.PlaceholderReplacer; +import pro.verron.officestamper.core.Placeholders; +import pro.verron.officestamper.preset.OfficeStamperConfigurations; +import pro.verron.officestamper.preset.OfficeStampers; import java.io.InputStream; import java.io.OutputStream; diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java index 45366b85..bcf97f97 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java @@ -13,11 +13,11 @@ import org.wickedsource.docxstamper.processor.replaceExpression.IReplaceWithProcessor; import org.wickedsource.docxstamper.processor.table.ITableResolver; import org.wickedsource.docxstamper.replace.typeresolver.TypeResolver; -import pro.verron.docxstamper.api.*; -import pro.verron.docxstamper.preset.CommentProcessorFactory; -import pro.verron.docxstamper.preset.OfficeStamperConfigurations; -import pro.verron.docxstamper.preset.Resolvers; -import pro.verron.docxstamper.preset.resolver.Null2DefaultResolver; +import pro.verron.officestamper.api.*; +import pro.verron.officestamper.preset.CommentProcessorFactory; +import pro.verron.officestamper.preset.OfficeStamperConfigurations; +import pro.verron.officestamper.preset.Resolvers; +import pro.verron.officestamper.preset.resolver.Null2DefaultResolver; import java.util.*; import java.util.function.Function; @@ -271,7 +271,7 @@ public DocxStamperConfiguration addCommentProcessor( * Creates a {@link DocxStamper} instance configured with this configuration. * * @return a {@link DocxStamper} object - * @deprecated use new {@link DocxStamper#DocxStamper(DocxStamperConfiguration)} instead + * @deprecated use new {@link DocxStamper#DocxStamper(OfficeStamperConfiguration)}} instead */ @Override @Deprecated(forRemoval = true, since = "1.6.4") diff --git a/src/main/java/org/wickedsource/docxstamper/api/DocxStamperException.java b/src/main/java/org/wickedsource/docxstamper/api/DocxStamperException.java index dd486c0b..47fecf26 100644 --- a/src/main/java/org/wickedsource/docxstamper/api/DocxStamperException.java +++ b/src/main/java/org/wickedsource/docxstamper/api/DocxStamperException.java @@ -1,6 +1,6 @@ package org.wickedsource.docxstamper.api; -import pro.verron.docxstamper.api.OfficeStamperException; +import pro.verron.officestamper.api.OfficeStamperException; /** * This class represents an exception that can be thrown during the processing of a Docx file using the DocxStamper library. diff --git a/src/main/java/org/wickedsource/docxstamper/api/EvaluationContextConfigurer.java b/src/main/java/org/wickedsource/docxstamper/api/EvaluationContextConfigurer.java index a4893daf..2bf33cc5 100644 --- a/src/main/java/org/wickedsource/docxstamper/api/EvaluationContextConfigurer.java +++ b/src/main/java/org/wickedsource/docxstamper/api/EvaluationContextConfigurer.java @@ -14,10 +14,10 @@ * @since 1.0.13 * @deprecated since 1.6.8, This class has been deprecated in the effort * of the library modularization. - * It is recommended to use the {@link pro.verron.docxstamper.api.EvaluationContextConfigurer} class instead. + * It is recommended to use the {@link pro.verron.officestamper.api.EvaluationContextConfigurer} class instead. * This class will not be exported in the future releases of the module. */ @Deprecated(since = "1.6.8", forRemoval = true) public interface EvaluationContextConfigurer - extends pro.verron.docxstamper.api.EvaluationContextConfigurer { + extends pro.verron.officestamper.api.EvaluationContextConfigurer { } diff --git a/src/main/java/org/wickedsource/docxstamper/api/UnresolvedExpressionException.java b/src/main/java/org/wickedsource/docxstamper/api/UnresolvedExpressionException.java index 314ff72f..887bcd77 100644 --- a/src/main/java/org/wickedsource/docxstamper/api/UnresolvedExpressionException.java +++ b/src/main/java/org/wickedsource/docxstamper/api/UnresolvedExpressionException.java @@ -1,6 +1,6 @@ package org.wickedsource.docxstamper.api; -import pro.verron.docxstamper.api.OfficeStamperException; +import pro.verron.officestamper.api.OfficeStamperException; /** * This exception is thrown if an expression could not be processed by any comment processor. diff --git a/src/main/java/org/wickedsource/docxstamper/api/commentprocessor/ICommentProcessor.java b/src/main/java/org/wickedsource/docxstamper/api/commentprocessor/ICommentProcessor.java index 7bf29185..81d20c82 100644 --- a/src/main/java/org/wickedsource/docxstamper/api/commentprocessor/ICommentProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/api/commentprocessor/ICommentProcessor.java @@ -1,6 +1,6 @@ package org.wickedsource.docxstamper.api.commentprocessor; -import pro.verron.docxstamper.api.CommentProcessor; +import pro.verron.officestamper.api.CommentProcessor; /** *

In a .docx template used by DocxStamper, you can comment paragraphs of text to manipulate them. The comments in diff --git a/src/main/java/org/wickedsource/docxstamper/api/preprocessor/PreProcessor.java b/src/main/java/org/wickedsource/docxstamper/api/preprocessor/PreProcessor.java index 79f7d32f..39270468 100644 --- a/src/main/java/org/wickedsource/docxstamper/api/preprocessor/PreProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/api/preprocessor/PreProcessor.java @@ -15,10 +15,10 @@ * @since 1.6.4 * @deprecated since 1.6.8, This class has been deprecated in the effort * of the library modularization. - * It is recommended to use the {@link pro.verron.docxstamper.api.PreProcessor} class instead. + * It is recommended to use the {@link pro.verron.officestamper.api.PreProcessor} class instead. * This class will not be exported in the future releases of the module. */ @Deprecated(since = "1.6.8", forRemoval = true) public interface PreProcessor - extends pro.verron.docxstamper.api.PreProcessor { + extends pro.verron.officestamper.api.PreProcessor { } diff --git a/src/main/java/org/wickedsource/docxstamper/api/typeresolver/ITypeResolver.java b/src/main/java/org/wickedsource/docxstamper/api/typeresolver/ITypeResolver.java index cee619fd..f374bb8f 100644 --- a/src/main/java/org/wickedsource/docxstamper/api/typeresolver/ITypeResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/api/typeresolver/ITypeResolver.java @@ -1,6 +1,6 @@ package org.wickedsource.docxstamper.api.typeresolver; -import pro.verron.docxstamper.api.ObjectResolver; +import pro.verron.officestamper.api.ObjectResolver; /** *

@@ -28,5 +28,5 @@ */ @Deprecated(since = "1.6.7", forRemoval = true) public interface ITypeResolver - extends pro.verron.docxstamper.api.ITypeResolver { + extends pro.verron.officestamper.api.ITypeResolver { } diff --git a/src/main/java/org/wickedsource/docxstamper/el/ExpressionResolver.java b/src/main/java/org/wickedsource/docxstamper/el/ExpressionResolver.java index 92a79bb2..f2c9625c 100644 --- a/src/main/java/org/wickedsource/docxstamper/el/ExpressionResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/el/ExpressionResolver.java @@ -4,7 +4,7 @@ import org.springframework.expression.spel.SpelParserConfiguration; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; -import pro.verron.docxstamper.api.Placeholder; +import pro.verron.officestamper.api.Placeholder; /** * Resolves expressions against a given context object. Expressions can be either SpEL expressions or simple property diff --git a/src/main/java/org/wickedsource/docxstamper/preprocessor/MergeSameStyleRuns.java b/src/main/java/org/wickedsource/docxstamper/preprocessor/MergeSameStyleRuns.java index 97c7322f..e21b1b63 100644 --- a/src/main/java/org/wickedsource/docxstamper/preprocessor/MergeSameStyleRuns.java +++ b/src/main/java/org/wickedsource/docxstamper/preprocessor/MergeSameStyleRuns.java @@ -6,7 +6,7 @@ import org.docx4j.wml.ContentAccessor; import org.docx4j.wml.R; import org.docx4j.wml.RPr; -import pro.verron.docxstamper.api.PreProcessor; +import pro.verron.officestamper.api.PreProcessor; import java.util.ArrayList; import java.util.LinkedHashSet; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java index a1f13591..8b044dd3 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/BaseCommentProcessor.java @@ -1,8 +1,8 @@ package org.wickedsource.docxstamper.processor; import org.wickedsource.docxstamper.DocxStamper; -import pro.verron.docxstamper.api.AbstractCommentProcessor; -import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; +import pro.verron.officestamper.api.AbstractCommentProcessor; +import pro.verron.officestamper.api.ParagraphPlaceholderReplacer; /** * Base class for comment processors. The current run and paragraph are set by the {@link DocxStamper} class. diff --git a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessingException.java b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessingException.java index 1560c8f6..eb77092d 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessingException.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessingException.java @@ -1,7 +1,7 @@ package org.wickedsource.docxstamper.processor; import org.docx4j.wml.P; -import pro.verron.docxstamper.api.OfficeStamperException; +import pro.verron.officestamper.api.OfficeStamperException; import static java.lang.String.format; import static org.docx4j.TextUtils.getText; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java index 4ae0cf95..abc330f5 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/CommentProcessorRegistry.java @@ -15,11 +15,11 @@ import org.wickedsource.docxstamper.el.ExpressionResolver; import org.wickedsource.docxstamper.util.RunUtil; import org.wickedsource.docxstamper.util.walk.BaseCoordinatesWalker; -import pro.verron.docxstamper.api.Comment; -import pro.verron.docxstamper.api.CommentProcessor; -import pro.verron.docxstamper.core.CommentUtil; -import pro.verron.docxstamper.core.Placeholders; -import pro.verron.docxstamper.core.StandardParagraph; +import pro.verron.officestamper.api.Comment; +import pro.verron.officestamper.api.CommentProcessor; +import pro.verron.officestamper.core.CommentUtil; +import pro.verron.officestamper.core.Placeholders; +import pro.verron.officestamper.core.StandardParagraph; import java.math.BigInteger; import java.util.ArrayList; @@ -27,8 +27,8 @@ import java.util.Objects; import java.util.Optional; -import static pro.verron.docxstamper.core.CommentUtil.getCommentString; -import static pro.verron.docxstamper.core.CommentUtil.getComments; +import static pro.verron.officestamper.core.CommentUtil.getCommentString; +import static pro.verron.officestamper.core.CommentUtil.getComments; /** * Allows registration of {@link ICommentProcessor} objects. Each registered diff --git a/src/main/java/org/wickedsource/docxstamper/processor/displayif/DisplayIfProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/displayif/DisplayIfProcessor.java index f9c89838..2f8e96be 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/displayif/DisplayIfProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/displayif/DisplayIfProcessor.java @@ -8,9 +8,9 @@ import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.processor.CommentProcessingException; import org.wickedsource.docxstamper.util.ObjectDeleter; -import pro.verron.docxstamper.api.CommentProcessor; -import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; -import pro.verron.docxstamper.core.PlaceholderReplacer; +import pro.verron.officestamper.api.CommentProcessor; +import pro.verron.officestamper.api.ParagraphPlaceholderReplacer; +import pro.verron.officestamper.core.PlaceholderReplacer; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java index 99d49d03..45f16955 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphRepeatProcessor.java @@ -3,15 +3,14 @@ import org.docx4j.XmlUtils; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.*; -import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.util.ParagraphUtil; import org.wickedsource.docxstamper.util.SectionUtil; -import pro.verron.docxstamper.api.*; -import pro.verron.docxstamper.core.CommentUtil; -import pro.verron.docxstamper.core.PlaceholderReplacer; -import pro.verron.docxstamper.core.StandardParagraph; -import pro.verron.docxstamper.preset.Resolvers; +import pro.verron.officestamper.api.*; +import pro.verron.officestamper.core.CommentUtil; +import pro.verron.officestamper.core.PlaceholderReplacer; +import pro.verron.officestamper.core.StandardParagraph; +import pro.verron.officestamper.preset.Resolvers; import java.math.BigInteger; import java.util.*; @@ -179,7 +178,7 @@ public void commitChanges(WordprocessingMLPackage document) { ContentAccessor parent = (ContentAccessor) currentP.getParent(); List parentContent = parent.getContent(); int index = parentContent.indexOf(currentP); - if (index < 0) throw new DocxStamperException("Impossible"); + if (index < 0) throw new OfficeStamperException("Impossible"); Paragraphs paragraphsToRepeat = entry.getValue(); Deque expressionContexts = Objects.requireNonNull( diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java index 75192002..1a7337f5 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/ParagraphResolverDocumentWalker.java @@ -4,8 +4,8 @@ import org.docx4j.wml.P; import org.docx4j.wml.Tr; import org.wickedsource.docxstamper.util.walk.BaseDocumentWalker; -import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; -import pro.verron.docxstamper.core.StandardParagraph; +import pro.verron.officestamper.api.ParagraphPlaceholderReplacer; +import pro.verron.officestamper.core.StandardParagraph; /** * Walks through a document and replaces expressions with values from the given diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java index 57535d4f..f124e8bc 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java @@ -10,9 +10,9 @@ import org.wickedsource.docxstamper.util.DocumentUtil; import org.wickedsource.docxstamper.util.ParagraphUtil; import org.wickedsource.docxstamper.util.SectionUtil; -import pro.verron.docxstamper.api.*; -import pro.verron.docxstamper.core.PlaceholderReplacer; -import pro.verron.docxstamper.preset.Resolvers; +import pro.verron.officestamper.api.*; +import pro.verron.officestamper.core.PlaceholderReplacer; +import pro.verron.officestamper.preset.Resolvers; import java.io.IOException; import java.io.OutputStream; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java index e07beec3..98b4d09b 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatProcessor.java @@ -5,11 +5,11 @@ import org.docx4j.wml.*; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.processor.CommentProcessingException; -import pro.verron.docxstamper.api.Comment; -import pro.verron.docxstamper.api.CommentProcessor; -import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; -import pro.verron.docxstamper.core.CommentUtil; -import pro.verron.docxstamper.core.PlaceholderReplacer; +import pro.verron.officestamper.api.Comment; +import pro.verron.officestamper.api.CommentProcessor; +import pro.verron.officestamper.api.ParagraphPlaceholderReplacer; +import pro.verron.officestamper.core.CommentUtil; +import pro.verron.officestamper.core.PlaceholderReplacer; import java.math.BigInteger; import java.util.*; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/replaceExpression/ReplaceWithProcessor.java b/src/main/java/org/wickedsource/docxstamper/processor/replaceExpression/ReplaceWithProcessor.java index f3f3d8ac..845d5987 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/replaceExpression/ReplaceWithProcessor.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/replaceExpression/ReplaceWithProcessor.java @@ -5,9 +5,9 @@ import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.util.RunUtil; -import pro.verron.docxstamper.api.CommentProcessor; -import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; -import pro.verron.docxstamper.core.PlaceholderReplacer; +import pro.verron.officestamper.api.CommentProcessor; +import pro.verron.officestamper.api.ParagraphPlaceholderReplacer; +import pro.verron.officestamper.core.PlaceholderReplacer; import java.util.List; import java.util.function.Function; @@ -42,8 +42,12 @@ private ReplaceWithProcessor( * * @param pr the placeholder replacer to use * @param nullReplacementValue a {@link String} object + * * @return the processor + * + * @deprecated should only be used internally by office stamper */ + @Deprecated(since = "1.6.8", forRemoval = true) public static CommentProcessor newInstance( PlaceholderReplacer pr, String nullReplacementValue diff --git a/src/main/java/org/wickedsource/docxstamper/processor/table/ITableResolver.java b/src/main/java/org/wickedsource/docxstamper/processor/table/ITableResolver.java index 8981c640..57197147 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/table/ITableResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/table/ITableResolver.java @@ -1,6 +1,6 @@ package org.wickedsource.docxstamper.processor.table; -import pro.verron.docxstamper.api.StampTable; +import pro.verron.officestamper.api.StampTable; /** * This interface is used to resolve a table in the template document. diff --git a/src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java b/src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java index 39c1d0c0..74ccdcd5 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java @@ -8,12 +8,12 @@ * @deprecated since 1.6.8, This class has been deprecated in the effort * of the library modularization. * It is recommended to use the - * {@link pro.verron.docxstamper.api.StampTable} class instead. + * {@link pro.verron.officestamper.api.StampTable} class instead. * This class will be removed in the future releases of the module. */ @Deprecated(since = "1.6.8", forRemoval = true) public class StampTable - extends pro.verron.docxstamper.api.StampTable { + extends pro.verron.officestamper.api.StampTable { /** * {@inheritDoc} */ diff --git a/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java b/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java index 79dfbf95..dcd8ded1 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java @@ -7,10 +7,10 @@ import org.wickedsource.docxstamper.processor.BaseCommentProcessor; import org.wickedsource.docxstamper.processor.CommentProcessingException; import org.wickedsource.docxstamper.util.ParagraphUtil; -import pro.verron.docxstamper.api.CommentProcessor; -import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; -import pro.verron.docxstamper.api.StampTable; -import pro.verron.docxstamper.core.PlaceholderReplacer; +import pro.verron.officestamper.api.CommentProcessor; +import pro.verron.officestamper.api.ParagraphPlaceholderReplacer; +import pro.verron.officestamper.api.StampTable; +import pro.verron.officestamper.core.PlaceholderReplacer; import java.util.Collections; import java.util.HashMap; @@ -45,7 +45,9 @@ private TableResolver( * @param pr a {@link PlaceholderReplacer} instance * @param nullReplacementValue in case the value to interpret is null * @return a new {@link TableResolver} instance + * @deprecated should be an internal implementation detail */ + @Deprecated(since = "1.6.8", forRemoval = true) public static CommentProcessor newInstance( PlaceholderReplacer pr, String nullReplacementValue diff --git a/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java b/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java index dc518ab0..16b6b03f 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/PlaceholderReplacer.java @@ -1,18 +1,18 @@ package org.wickedsource.docxstamper.replace; import org.wickedsource.docxstamper.el.ExpressionResolver; -import pro.verron.docxstamper.api.Placeholder; -import pro.verron.docxstamper.core.ObjectResolverRegistry; +import pro.verron.officestamper.api.Placeholder; +import pro.verron.officestamper.core.ObjectResolverRegistry; /** * @deprecated since 1.6.8, This class has been deprecated in the effort * of the library modularization. - * It is recommended to use the {@link pro.verron.docxstamper.core.PlaceholderReplacer} class instead. + * It is recommended to use the {@link pro.verron.officestamper.core.PlaceholderReplacer} class instead. * This class will not be exported in the future releases of the module. */ @Deprecated(since = "1.6.8", forRemoval = true) public class PlaceholderReplacer - extends pro.verron.docxstamper.core.PlaceholderReplacer { + extends pro.verron.officestamper.core.PlaceholderReplacer { /** *

Constructor for PlaceholderReplacer.

* diff --git a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/LegacyFallbackResolver.java b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/LegacyFallbackResolver.java index 5a652d13..6097e288 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/LegacyFallbackResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/LegacyFallbackResolver.java @@ -3,8 +3,8 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; import org.wickedsource.docxstamper.util.RunUtil; -import pro.verron.docxstamper.api.ObjectResolver; -import pro.verron.docxstamper.preset.Resolvers; +import pro.verron.officestamper.api.ObjectResolver; +import pro.verron.officestamper.preset.Resolvers; /** * The LegacyFallbackResolver served as a fallback when there was no ITypeResolver available for a certain type. diff --git a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/TypeResolver.java b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/TypeResolver.java index e6c1147c..5105c4b7 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/TypeResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/TypeResolver.java @@ -3,8 +3,8 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; import org.wickedsource.docxstamper.api.DocxStamperException; -import pro.verron.docxstamper.api.ITypeResolver; -import pro.verron.docxstamper.api.ObjectResolver; +import pro.verron.officestamper.api.ITypeResolver; +import pro.verron.officestamper.api.ObjectResolver; /** * The TypeResolver class is responsible for resolving objects of a specified type to objects of the DOCX4J API diff --git a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java index 8c4e02bf..a2dfbfed 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java @@ -14,12 +14,12 @@ * @deprecated since 1.6.8, This class has been deprecated in the effort * of the library modularization. * It is recommended to use the - * {@link pro.verron.docxstamper.api.Image} class instead. + * {@link pro.verron.officestamper.api.Image} class instead. * This class will not be exported in the future releases of the module. */ @Deprecated(since = "1.6.8", forRemoval = true) public final class Image - extends pro.verron.docxstamper.api.Image { + extends pro.verron.officestamper.api.Image { /** *

Constructor for Image.

diff --git a/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java b/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java index 3ca3c152..60e6194c 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java +++ b/src/main/java/org/wickedsource/docxstamper/util/CommentUtil.java @@ -4,12 +4,12 @@ * @deprecated since 1.6.8, This class has been deprecated in the effort * of the library modularization. * It is recommended to use the - * {@link pro.verron.docxstamper.core.CommentUtil} class instead. + * {@link pro.verron.officestamper.core.CommentUtil} class instead. * This class will be removed in the future releases of the module. */ @Deprecated(since = "1.6.8", forRemoval = true) public class CommentUtil - extends pro.verron.docxstamper.core.CommentUtil { + extends pro.verron.officestamper.core.CommentUtil { private CommentUtil() {} } diff --git a/src/main/java/org/wickedsource/docxstamper/util/CommentWrapper.java b/src/main/java/org/wickedsource/docxstamper/util/CommentWrapper.java index 484b8dfc..4a13447d 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/CommentWrapper.java +++ b/src/main/java/org/wickedsource/docxstamper/util/CommentWrapper.java @@ -1,19 +1,25 @@ package org.wickedsource.docxstamper.util; -import pro.verron.docxstamper.core.StandardComment; +import pro.verron.officestamper.api.Comment; +import pro.verron.officestamper.core.StandardComment; /** * @deprecated since 1.6.8, This class has been deprecated in the effort * of the library modularization. * It is recommended to use the - * {@link pro.verron.docxstamper.core.StandardComment} class and - * {@link pro.verron.docxstamper.api.Comment} interface + * {@link StandardComment} class and + * {@link Comment} interface * instead. * This class will be moved to internals in the future releases of the module. */ @Deprecated(since = "1.6.8", forRemoval = true) public class CommentWrapper extends StandardComment { + + /** + * The CommentWrapper class is a subclass of StandardComment that represents a comment wrapper in a Word document. + * This class extends StandardComment and does not accommodate the feature needing direct document access. + */ public CommentWrapper() { super(null); } diff --git a/src/main/java/org/wickedsource/docxstamper/util/DocumentUtil.java b/src/main/java/org/wickedsource/docxstamper/util/DocumentUtil.java index 0cc9e5d3..bfef27cd 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/DocumentUtil.java +++ b/src/main/java/org/wickedsource/docxstamper/util/DocumentUtil.java @@ -10,7 +10,7 @@ import org.docx4j.openpackaging.parts.relationships.RelationshipsPart; import org.docx4j.wml.*; import org.jvnet.jaxb2_commons.ppp.Child; -import pro.verron.docxstamper.api.OfficeStamperException; +import pro.verron.officestamper.api.OfficeStamperException; import java.util.*; import java.util.stream.Stream; @@ -267,6 +267,16 @@ public static Stream

streamParagraphs(WordprocessingMLPackage document) { return streamElements(document, P.class); } + /** + * Finds the smallest common parent between two objects. + * + * @param o1 the first object + * @param o2 the second object + * + * @return the smallest common parent of the two objects + * + * @throws OfficeStamperException if there is an error finding the common parent + */ public static ContentAccessor findSmallestCommonParent(Object o1, Object o2) { if (depthElementSearch(o1, o2) && o2 instanceof ContentAccessor contentAccessor) return findInsertableParent(contentAccessor); @@ -276,6 +286,14 @@ else if (o2 instanceof Child child) throw new OfficeStamperException(); } + /** + * Recursively searches for an element in a content tree. + * + * @param searchTarget the element to search for + * @param content the content tree to search in + * + * @return true if the element is found, false otherwise + */ public static boolean depthElementSearch(Object searchTarget, Object content) { content = XmlUtils.unwrap(content); if (searchTarget.equals(content)) { diff --git a/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java b/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java index 37065d09..44168935 100644 --- a/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java +++ b/src/main/java/org/wickedsource/docxstamper/util/RunUtil.java @@ -7,7 +7,7 @@ import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage; import org.docx4j.wml.*; import org.wickedsource.docxstamper.api.DocxStamperException; -import pro.verron.docxstamper.api.OfficeStamperException; +import pro.verron.officestamper.api.OfficeStamperException; import java.util.Objects; import java.util.Random; diff --git a/src/main/java/pro/verron/docxstamper/StamperFactory.java b/src/main/java/pro/verron/officestamper/StamperFactory.java similarity index 64% rename from src/main/java/pro/verron/docxstamper/StamperFactory.java rename to src/main/java/pro/verron/officestamper/StamperFactory.java index 47669631..6d878969 100644 --- a/src/main/java/pro/verron/docxstamper/StamperFactory.java +++ b/src/main/java/pro/verron/officestamper/StamperFactory.java @@ -1,12 +1,13 @@ -package pro.verron.docxstamper; +package pro.verron.officestamper; -import pro.verron.docxstamper.preset.OfficeStampers; +import pro.verron.officestamper.core.CommentUtil; +import pro.verron.officestamper.preset.OfficeStampers; /** * @deprecated since 1.6.8, This class has been deprecated in the effort * of the library modularization. * It is recommended to use the - * {@link pro.verron.docxstamper.core.CommentUtil} class instead. + * {@link CommentUtil} class instead. * This class will be removed in the future releases of the module. */ @Deprecated(since = "1.6.8", forRemoval = true) diff --git a/src/main/java/pro/verron/docxstamper/api/AbstractCommentProcessor.java b/src/main/java/pro/verron/officestamper/api/AbstractCommentProcessor.java similarity index 98% rename from src/main/java/pro/verron/docxstamper/api/AbstractCommentProcessor.java rename to src/main/java/pro/verron/officestamper/api/AbstractCommentProcessor.java index 96aa5453..8054666e 100644 --- a/src/main/java/pro/verron/docxstamper/api/AbstractCommentProcessor.java +++ b/src/main/java/pro/verron/officestamper/api/AbstractCommentProcessor.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.api; +package pro.verron.officestamper.api; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.P; diff --git a/src/main/java/pro/verron/docxstamper/api/Comment.java b/src/main/java/pro/verron/officestamper/api/Comment.java similarity index 98% rename from src/main/java/pro/verron/docxstamper/api/Comment.java rename to src/main/java/pro/verron/officestamper/api/Comment.java index 5b3927ef..bf709394 100644 --- a/src/main/java/pro/verron/docxstamper/api/Comment.java +++ b/src/main/java/pro/verron/officestamper/api/Comment.java @@ -1,8 +1,8 @@ -package pro.verron.docxstamper.api; +package pro.verron.officestamper.api; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.*; -import pro.verron.docxstamper.core.CommentUtil; +import pro.verron.officestamper.core.CommentUtil; import java.util.List; import java.util.Set; diff --git a/src/main/java/pro/verron/docxstamper/api/CommentProcessor.java b/src/main/java/pro/verron/officestamper/api/CommentProcessor.java similarity index 98% rename from src/main/java/pro/verron/docxstamper/api/CommentProcessor.java rename to src/main/java/pro/verron/officestamper/api/CommentProcessor.java index 9e37803d..ce22ed68 100644 --- a/src/main/java/pro/verron/docxstamper/api/CommentProcessor.java +++ b/src/main/java/pro/verron/officestamper/api/CommentProcessor.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.api; +package pro.verron.officestamper.api; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.P; diff --git a/src/main/java/pro/verron/docxstamper/api/EvaluationContextConfigurer.java b/src/main/java/pro/verron/officestamper/api/EvaluationContextConfigurer.java similarity index 89% rename from src/main/java/pro/verron/docxstamper/api/EvaluationContextConfigurer.java rename to src/main/java/pro/verron/officestamper/api/EvaluationContextConfigurer.java index f6355f51..b279dee1 100644 --- a/src/main/java/pro/verron/docxstamper/api/EvaluationContextConfigurer.java +++ b/src/main/java/pro/verron/officestamper/api/EvaluationContextConfigurer.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.api; +package pro.verron.officestamper.api; import org.springframework.expression.spel.support.StandardEvaluationContext; diff --git a/src/main/java/pro/verron/docxstamper/api/ITypeResolver.java b/src/main/java/pro/verron/officestamper/api/ITypeResolver.java similarity index 98% rename from src/main/java/pro/verron/docxstamper/api/ITypeResolver.java rename to src/main/java/pro/verron/officestamper/api/ITypeResolver.java index 1870b44a..181b9d02 100644 --- a/src/main/java/pro/verron/docxstamper/api/ITypeResolver.java +++ b/src/main/java/pro/verron/officestamper/api/ITypeResolver.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.api; +package pro.verron.officestamper.api; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; diff --git a/src/main/java/pro/verron/docxstamper/api/Image.java b/src/main/java/pro/verron/officestamper/api/Image.java similarity index 98% rename from src/main/java/pro/verron/docxstamper/api/Image.java rename to src/main/java/pro/verron/officestamper/api/Image.java index 733dce08..b990e69c 100644 --- a/src/main/java/pro/verron/docxstamper/api/Image.java +++ b/src/main/java/pro/verron/officestamper/api/Image.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.api; +package pro.verron.officestamper.api; import org.apache.commons.io.IOUtils; diff --git a/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java b/src/main/java/pro/verron/officestamper/api/ObjectResolver.java similarity index 98% rename from src/main/java/pro/verron/docxstamper/api/ObjectResolver.java rename to src/main/java/pro/verron/officestamper/api/ObjectResolver.java index b4353b12..c096090c 100644 --- a/src/main/java/pro/verron/docxstamper/api/ObjectResolver.java +++ b/src/main/java/pro/verron/officestamper/api/ObjectResolver.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.api; +package pro.verron.officestamper.api; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; diff --git a/src/main/java/pro/verron/docxstamper/api/OfficeStamper.java b/src/main/java/pro/verron/officestamper/api/OfficeStamper.java similarity index 95% rename from src/main/java/pro/verron/docxstamper/api/OfficeStamper.java rename to src/main/java/pro/verron/officestamper/api/OfficeStamper.java index 47746d50..3f082780 100644 --- a/src/main/java/pro/verron/docxstamper/api/OfficeStamper.java +++ b/src/main/java/pro/verron/officestamper/api/OfficeStamper.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.api; +package pro.verron.officestamper.api; import org.docx4j.openpackaging.packages.OpcPackage; diff --git a/src/main/java/pro/verron/docxstamper/api/OfficeStamperConfiguration.java b/src/main/java/pro/verron/officestamper/api/OfficeStamperConfiguration.java similarity index 99% rename from src/main/java/pro/verron/docxstamper/api/OfficeStamperConfiguration.java rename to src/main/java/pro/verron/officestamper/api/OfficeStamperConfiguration.java index 235cf4d5..56d131a6 100644 --- a/src/main/java/pro/verron/docxstamper/api/OfficeStamperConfiguration.java +++ b/src/main/java/pro/verron/officestamper/api/OfficeStamperConfiguration.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.api; +package pro.verron.officestamper.api; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.springframework.expression.spel.SpelParserConfiguration; diff --git a/src/main/java/pro/verron/docxstamper/api/OfficeStamperException.java b/src/main/java/pro/verron/officestamper/api/OfficeStamperException.java similarity index 97% rename from src/main/java/pro/verron/docxstamper/api/OfficeStamperException.java rename to src/main/java/pro/verron/officestamper/api/OfficeStamperException.java index 6419d24d..525c8dd5 100644 --- a/src/main/java/pro/verron/docxstamper/api/OfficeStamperException.java +++ b/src/main/java/pro/verron/officestamper/api/OfficeStamperException.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.api; +package pro.verron.officestamper.api; /** * OfficeStamperException is a subclass of RuntimeException that represents an exception that can be thrown during the diff --git a/src/main/java/pro/verron/docxstamper/api/Paragraph.java b/src/main/java/pro/verron/officestamper/api/Paragraph.java similarity index 96% rename from src/main/java/pro/verron/docxstamper/api/Paragraph.java rename to src/main/java/pro/verron/officestamper/api/Paragraph.java index eaf209a1..65a891fe 100644 --- a/src/main/java/pro/verron/docxstamper/api/Paragraph.java +++ b/src/main/java/pro/verron/officestamper/api/Paragraph.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.api; +package pro.verron.officestamper.api; import org.docx4j.wml.R; diff --git a/src/main/java/pro/verron/docxstamper/api/ParagraphPlaceholderReplacer.java b/src/main/java/pro/verron/officestamper/api/ParagraphPlaceholderReplacer.java similarity index 95% rename from src/main/java/pro/verron/docxstamper/api/ParagraphPlaceholderReplacer.java rename to src/main/java/pro/verron/officestamper/api/ParagraphPlaceholderReplacer.java index 79f09819..466f0f2e 100644 --- a/src/main/java/pro/verron/docxstamper/api/ParagraphPlaceholderReplacer.java +++ b/src/main/java/pro/verron/officestamper/api/ParagraphPlaceholderReplacer.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.api; +package pro.verron.officestamper.api; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; diff --git a/src/main/java/pro/verron/docxstamper/api/Placeholder.java b/src/main/java/pro/verron/officestamper/api/Placeholder.java similarity index 92% rename from src/main/java/pro/verron/docxstamper/api/Placeholder.java rename to src/main/java/pro/verron/officestamper/api/Placeholder.java index ef7cfd1c..f0f13a16 100644 --- a/src/main/java/pro/verron/docxstamper/api/Placeholder.java +++ b/src/main/java/pro/verron/officestamper/api/Placeholder.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.api; +package pro.verron.officestamper.api; /** * The Placeholder interface represents a placeholder in a text document. diff --git a/src/main/java/pro/verron/docxstamper/api/PreProcessor.java b/src/main/java/pro/verron/officestamper/api/PreProcessor.java similarity index 91% rename from src/main/java/pro/verron/docxstamper/api/PreProcessor.java rename to src/main/java/pro/verron/officestamper/api/PreProcessor.java index 4347f62a..cb5fdf8b 100644 --- a/src/main/java/pro/verron/docxstamper/api/PreProcessor.java +++ b/src/main/java/pro/verron/officestamper/api/PreProcessor.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.api; +package pro.verron.officestamper.api; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; diff --git a/src/main/java/pro/verron/docxstamper/api/StampTable.java b/src/main/java/pro/verron/officestamper/api/StampTable.java similarity index 97% rename from src/main/java/pro/verron/docxstamper/api/StampTable.java rename to src/main/java/pro/verron/officestamper/api/StampTable.java index 2c52002a..2c17a982 100644 --- a/src/main/java/pro/verron/docxstamper/api/StampTable.java +++ b/src/main/java/pro/verron/officestamper/api/StampTable.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.api; +package pro.verron.officestamper.api; import org.springframework.lang.NonNull; diff --git a/src/main/java/pro/verron/docxstamper/api/StreamStamper.java b/src/main/java/pro/verron/officestamper/api/StreamStamper.java similarity index 98% rename from src/main/java/pro/verron/docxstamper/api/StreamStamper.java rename to src/main/java/pro/verron/officestamper/api/StreamStamper.java index 4034d52b..3c195d08 100644 --- a/src/main/java/pro/verron/docxstamper/api/StreamStamper.java +++ b/src/main/java/pro/verron/officestamper/api/StreamStamper.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.api; +package pro.verron.officestamper.api; import org.docx4j.openpackaging.packages.OpcPackage; diff --git a/src/main/java/pro/verron/docxstamper/api/StringResolver.java b/src/main/java/pro/verron/officestamper/api/StringResolver.java similarity index 98% rename from src/main/java/pro/verron/docxstamper/api/StringResolver.java rename to src/main/java/pro/verron/officestamper/api/StringResolver.java index 40c9ef20..f82173a0 100644 --- a/src/main/java/pro/verron/docxstamper/api/StringResolver.java +++ b/src/main/java/pro/verron/officestamper/api/StringResolver.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.api; +package pro.verron.officestamper.api; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; diff --git a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java b/src/main/java/pro/verron/officestamper/core/CommentUtil.java similarity index 98% rename from src/main/java/pro/verron/docxstamper/core/CommentUtil.java rename to src/main/java/pro/verron/officestamper/core/CommentUtil.java index 3f275b0a..e1404996 100644 --- a/src/main/java/pro/verron/docxstamper/core/CommentUtil.java +++ b/src/main/java/pro/verron/officestamper/core/CommentUtil.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.core; +package pro.verron.officestamper.core; import org.docx4j.TextUtils; import org.docx4j.XmlUtils; @@ -15,9 +15,9 @@ import org.wickedsource.docxstamper.util.DocumentUtil; import org.wickedsource.docxstamper.util.walk.BaseDocumentWalker; import org.wickedsource.docxstamper.util.walk.DocumentWalker; -import pro.verron.docxstamper.api.Comment; -import pro.verron.docxstamper.api.OfficeStamperException; -import pro.verron.docxstamper.api.Placeholder; +import pro.verron.officestamper.api.Comment; +import pro.verron.officestamper.api.OfficeStamperException; +import pro.verron.officestamper.api.Placeholder; import java.math.BigInteger; import java.util.*; diff --git a/src/main/java/pro/verron/docxstamper/core/ObjectResolverRegistry.java b/src/main/java/pro/verron/officestamper/core/ObjectResolverRegistry.java similarity index 92% rename from src/main/java/pro/verron/docxstamper/core/ObjectResolverRegistry.java rename to src/main/java/pro/verron/officestamper/core/ObjectResolverRegistry.java index b26a4211..8f02365b 100644 --- a/src/main/java/pro/verron/docxstamper/core/ObjectResolverRegistry.java +++ b/src/main/java/pro/verron/officestamper/core/ObjectResolverRegistry.java @@ -1,10 +1,10 @@ -package pro.verron.docxstamper.core; +package pro.verron.officestamper.core; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; import org.wickedsource.docxstamper.api.DocxStamperException; -import pro.verron.docxstamper.api.ObjectResolver; -import pro.verron.docxstamper.api.Placeholder; +import pro.verron.officestamper.api.ObjectResolver; +import pro.verron.officestamper.api.Placeholder; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java b/src/main/java/pro/verron/officestamper/core/PlaceholderReplacer.java similarity index 96% rename from src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java rename to src/main/java/pro/verron/officestamper/core/PlaceholderReplacer.java index ed5bc4f3..b4946aa9 100644 --- a/src/main/java/pro/verron/docxstamper/core/PlaceholderReplacer.java +++ b/src/main/java/pro/verron/officestamper/core/PlaceholderReplacer.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.core; +package pro.verron.officestamper.core; import org.docx4j.jaxb.Context; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; @@ -12,10 +12,10 @@ import org.wickedsource.docxstamper.el.ExpressionResolver; import org.wickedsource.docxstamper.util.RunUtil; import org.wickedsource.docxstamper.util.walk.BaseCoordinatesWalker; -import pro.verron.docxstamper.api.OfficeStamperException; -import pro.verron.docxstamper.api.Paragraph; -import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; -import pro.verron.docxstamper.api.Placeholder; +import pro.verron.officestamper.api.OfficeStamperException; +import pro.verron.officestamper.api.Paragraph; +import pro.verron.officestamper.api.ParagraphPlaceholderReplacer; +import pro.verron.officestamper.api.Placeholder; /** * Replaces expressions in a document with the values provided by the {@link ExpressionResolver}. diff --git a/src/main/java/pro/verron/docxstamper/core/Placeholders.java b/src/main/java/pro/verron/officestamper/core/Placeholders.java similarity index 93% rename from src/main/java/pro/verron/docxstamper/core/Placeholders.java rename to src/main/java/pro/verron/officestamper/core/Placeholders.java index c22d6672..0f2b923e 100644 --- a/src/main/java/pro/verron/docxstamper/core/Placeholders.java +++ b/src/main/java/pro/verron/officestamper/core/Placeholders.java @@ -1,10 +1,10 @@ -package pro.verron.docxstamper.core; +package pro.verron.officestamper.core; import org.wickedsource.docxstamper.api.DocxStamperException; -import pro.verron.docxstamper.api.Paragraph; -import pro.verron.docxstamper.api.Placeholder; -import pro.verron.docxstamper.core.expression.Matcher; -import pro.verron.docxstamper.core.expression.PlaceholderFinder; +import pro.verron.officestamper.api.Paragraph; +import pro.verron.officestamper.api.Placeholder; +import pro.verron.officestamper.core.expression.Matcher; +import pro.verron.officestamper.core.expression.PlaceholderFinder; import java.util.List; import java.util.regex.Pattern; diff --git a/src/main/java/pro/verron/docxstamper/core/StandardComment.java b/src/main/java/pro/verron/officestamper/core/StandardComment.java similarity index 97% rename from src/main/java/pro/verron/docxstamper/core/StandardComment.java rename to src/main/java/pro/verron/officestamper/core/StandardComment.java index 8f00d65d..ab69278c 100644 --- a/src/main/java/pro/verron/docxstamper/core/StandardComment.java +++ b/src/main/java/pro/verron/officestamper/core/StandardComment.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.core; +package pro.verron.officestamper.core; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.CommentRangeEnd; @@ -7,8 +7,8 @@ import org.docx4j.wml.ContentAccessor; import org.docx4j.wml.R.CommentReference; import org.wickedsource.docxstamper.util.DocumentUtil; -import pro.verron.docxstamper.api.Comment; -import pro.verron.docxstamper.api.OfficeStamperException; +import pro.verron.officestamper.api.Comment; +import pro.verron.officestamper.api.OfficeStamperException; import java.util.ArrayList; import java.util.HashSet; diff --git a/src/main/java/pro/verron/docxstamper/core/StandardParagraph.java b/src/main/java/pro/verron/officestamper/core/StandardParagraph.java similarity index 98% rename from src/main/java/pro/verron/docxstamper/core/StandardParagraph.java rename to src/main/java/pro/verron/officestamper/core/StandardParagraph.java index e75c8bd8..2e8a20bf 100644 --- a/src/main/java/pro/verron/docxstamper/core/StandardParagraph.java +++ b/src/main/java/pro/verron/officestamper/core/StandardParagraph.java @@ -1,11 +1,11 @@ -package pro.verron.docxstamper.core; +package pro.verron.officestamper.core; import org.docx4j.wml.P; import org.docx4j.wml.R; import org.wickedsource.docxstamper.util.IndexedRun; import org.wickedsource.docxstamper.util.RunUtil; -import pro.verron.docxstamper.api.Paragraph; -import pro.verron.docxstamper.api.Placeholder; +import pro.verron.officestamper.api.Paragraph; +import pro.verron.officestamper.api.Placeholder; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/pro/verron/docxstamper/core/StandardPlaceholder.java b/src/main/java/pro/verron/officestamper/core/StandardPlaceholder.java similarity index 78% rename from src/main/java/pro/verron/docxstamper/core/StandardPlaceholder.java rename to src/main/java/pro/verron/officestamper/core/StandardPlaceholder.java index a61684d0..e5da5342 100644 --- a/src/main/java/pro/verron/docxstamper/core/StandardPlaceholder.java +++ b/src/main/java/pro/verron/officestamper/core/StandardPlaceholder.java @@ -1,7 +1,7 @@ -package pro.verron.docxstamper.core; +package pro.verron.officestamper.core; -import pro.verron.docxstamper.api.Placeholder; -import pro.verron.docxstamper.core.expression.Matcher; +import pro.verron.officestamper.api.Placeholder; +import pro.verron.officestamper.core.expression.Matcher; /** * Represents an expression with a configured Matcher. diff --git a/src/main/java/pro/verron/docxstamper/core/expression/Matcher.java b/src/main/java/pro/verron/officestamper/core/expression/Matcher.java similarity index 96% rename from src/main/java/pro/verron/docxstamper/core/expression/Matcher.java rename to src/main/java/pro/verron/officestamper/core/expression/Matcher.java index 7fba876c..8e2bdf96 100644 --- a/src/main/java/pro/verron/docxstamper/core/expression/Matcher.java +++ b/src/main/java/pro/verron/officestamper/core/expression/Matcher.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.core.expression; +package pro.verron.officestamper.core.expression; /** diff --git a/src/main/java/pro/verron/docxstamper/core/expression/PlaceholderFinder.java b/src/main/java/pro/verron/officestamper/core/expression/PlaceholderFinder.java similarity index 88% rename from src/main/java/pro/verron/docxstamper/core/expression/PlaceholderFinder.java rename to src/main/java/pro/verron/officestamper/core/expression/PlaceholderFinder.java index 29b3557a..a235286e 100644 --- a/src/main/java/pro/verron/docxstamper/core/expression/PlaceholderFinder.java +++ b/src/main/java/pro/verron/officestamper/core/expression/PlaceholderFinder.java @@ -1,7 +1,7 @@ -package pro.verron.docxstamper.core.expression; +package pro.verron.officestamper.core.expression; -import pro.verron.docxstamper.api.Placeholder; -import pro.verron.docxstamper.core.StandardPlaceholder; +import pro.verron.officestamper.api.Placeholder; +import pro.verron.officestamper.core.StandardPlaceholder; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/pro/verron/docxstamper/core/expression/package-info.java b/src/main/java/pro/verron/officestamper/core/expression/package-info.java similarity index 53% rename from src/main/java/pro/verron/docxstamper/core/expression/package-info.java rename to src/main/java/pro/verron/officestamper/core/expression/package-info.java index e26a8f26..faaaacb7 100644 --- a/src/main/java/pro/verron/docxstamper/core/expression/package-info.java +++ b/src/main/java/pro/verron/officestamper/core/expression/package-info.java @@ -1,4 +1,4 @@ @NonNullApi -package pro.verron.docxstamper.core.expression; +package pro.verron.officestamper.core.expression; import org.springframework.lang.NonNullApi; diff --git a/src/main/java/pro/verron/officestamper/experimental/ExcelParagraph.java b/src/main/java/pro/verron/officestamper/experimental/ExcelParagraph.java index edeb68fe..18ea1e37 100644 --- a/src/main/java/pro/verron/officestamper/experimental/ExcelParagraph.java +++ b/src/main/java/pro/verron/officestamper/experimental/ExcelParagraph.java @@ -1,7 +1,7 @@ package pro.verron.officestamper.experimental; import org.xlsx4j.sml.CTRst; -import pro.verron.docxstamper.api.Placeholder; +import pro.verron.officestamper.api.Placeholder; /** * The ExcelParagraph class represents a paragraph in an Excel document. diff --git a/src/main/java/pro/verron/officestamper/experimental/ExcelStamper.java b/src/main/java/pro/verron/officestamper/experimental/ExcelStamper.java index cec1a204..ad6add93 100644 --- a/src/main/java/pro/verron/officestamper/experimental/ExcelStamper.java +++ b/src/main/java/pro/verron/officestamper/experimental/ExcelStamper.java @@ -6,12 +6,12 @@ import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.xlsx4j.sml.CTRst; -import pro.verron.docxstamper.api.OfficeStamper; -import pro.verron.docxstamper.api.OfficeStamperException; +import pro.verron.officestamper.api.OfficeStamper; +import pro.verron.officestamper.api.OfficeStamperException; import java.io.OutputStream; -import static pro.verron.docxstamper.core.Placeholders.findVariables; +import static pro.verron.officestamper.core.Placeholders.findVariables; /** * The ExcelStamper class is an implementation of the OfficeStamper interface for stamping Excel templates. diff --git a/src/main/java/pro/verron/officestamper/experimental/ExcelVisitor.java b/src/main/java/pro/verron/officestamper/experimental/ExcelVisitor.java index dbf5441b..f2c7fb05 100644 --- a/src/main/java/pro/verron/officestamper/experimental/ExcelVisitor.java +++ b/src/main/java/pro/verron/officestamper/experimental/ExcelVisitor.java @@ -15,9 +15,8 @@ import org.slf4j.LoggerFactory; import org.springframework.lang.Nullable; import org.xlsx4j.sml.*; -import pro.verron.docxstamper.api.OfficeStamperException; +import pro.verron.officestamper.api.OfficeStamperException; -import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -42,7 +41,6 @@ private static void ignore(@Nullable Object ignored1) { } public final void visit(@Nullable Object object) { - before(object); try { if (object instanceof SpreadsheetMLPackage element) visit(element.getParts()); diff --git a/src/main/java/pro/verron/officestamper/experimental/PowerpointParagraph.java b/src/main/java/pro/verron/officestamper/experimental/PowerpointParagraph.java index 955b887d..175175df 100644 --- a/src/main/java/pro/verron/officestamper/experimental/PowerpointParagraph.java +++ b/src/main/java/pro/verron/officestamper/experimental/PowerpointParagraph.java @@ -3,8 +3,8 @@ import org.docx4j.dml.CTRegularTextRun; import org.docx4j.dml.CTTextCharacterProperties; import org.docx4j.dml.CTTextParagraph; -import pro.verron.docxstamper.api.Paragraph; -import pro.verron.docxstamper.api.Placeholder; +import pro.verron.officestamper.api.Paragraph; +import pro.verron.officestamper.api.Placeholder; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/pro/verron/officestamper/experimental/PowerpointStamper.java b/src/main/java/pro/verron/officestamper/experimental/PowerpointStamper.java index 32a5a55c..26b6d94e 100644 --- a/src/main/java/pro/verron/officestamper/experimental/PowerpointStamper.java +++ b/src/main/java/pro/verron/officestamper/experimental/PowerpointStamper.java @@ -7,9 +7,9 @@ import org.springframework.expression.spel.SpelParserConfiguration; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; -import pro.verron.docxstamper.api.OfficeStamper; -import pro.verron.docxstamper.api.OfficeStamperException; -import pro.verron.docxstamper.core.Placeholders; +import pro.verron.officestamper.api.OfficeStamper; +import pro.verron.officestamper.api.OfficeStamperException; +import pro.verron.officestamper.core.Placeholders; import java.io.OutputStream; import java.util.List; diff --git a/src/main/java/pro/verron/officestamper/experimental/PowerpointVisitor.java b/src/main/java/pro/verron/officestamper/experimental/PowerpointVisitor.java index efbd5f6d..565eaa69 100644 --- a/src/main/java/pro/verron/officestamper/experimental/PowerpointVisitor.java +++ b/src/main/java/pro/verron/officestamper/experimental/PowerpointVisitor.java @@ -12,7 +12,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.lang.Nullable; -import pro.verron.docxstamper.api.OfficeStamperException; +import pro.verron.officestamper.api.OfficeStamperException; import java.util.Arrays; import java.util.List; diff --git a/src/main/java/pro/verron/docxstamper/preset/CommentProcessorFactory.java b/src/main/java/pro/verron/officestamper/preset/CommentProcessorFactory.java similarity index 90% rename from src/main/java/pro/verron/docxstamper/preset/CommentProcessorFactory.java rename to src/main/java/pro/verron/officestamper/preset/CommentProcessorFactory.java index 0f4b2ee4..476b1920 100644 --- a/src/main/java/pro/verron/docxstamper/preset/CommentProcessorFactory.java +++ b/src/main/java/pro/verron/officestamper/preset/CommentProcessorFactory.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.preset; +package pro.verron.officestamper.preset; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.wickedsource.docxstamper.DocxStamper; @@ -9,11 +9,11 @@ import org.wickedsource.docxstamper.processor.repeat.RepeatProcessor; import org.wickedsource.docxstamper.processor.replaceExpression.ReplaceWithProcessor; import org.wickedsource.docxstamper.processor.table.TableResolver; -import pro.verron.docxstamper.api.CommentProcessor; -import pro.verron.docxstamper.api.OfficeStamper; -import pro.verron.docxstamper.api.OfficeStamperConfiguration; -import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; -import pro.verron.docxstamper.core.PlaceholderReplacer; +import pro.verron.officestamper.api.CommentProcessor; +import pro.verron.officestamper.api.OfficeStamper; +import pro.verron.officestamper.api.OfficeStamperConfiguration; +import pro.verron.officestamper.api.ParagraphPlaceholderReplacer; +import pro.verron.officestamper.core.PlaceholderReplacer; /** * Factory class to create the correct comment processor for a given comment. diff --git a/src/main/java/pro/verron/docxstamper/preset/EvaluationContextConfigurers.java b/src/main/java/pro/verron/officestamper/preset/EvaluationContextConfigurers.java similarity index 69% rename from src/main/java/pro/verron/docxstamper/preset/EvaluationContextConfigurers.java rename to src/main/java/pro/verron/officestamper/preset/EvaluationContextConfigurers.java index 854c051d..eb602f86 100644 --- a/src/main/java/pro/verron/docxstamper/preset/EvaluationContextConfigurers.java +++ b/src/main/java/pro/verron/officestamper/preset/EvaluationContextConfigurers.java @@ -1,8 +1,8 @@ -package pro.verron.docxstamper.preset; +package pro.verron.officestamper.preset; import org.wickedsource.docxstamper.el.NoOpEvaluationContextConfigurer; -import pro.verron.docxstamper.api.EvaluationContextConfigurer; +import pro.verron.officestamper.api.EvaluationContextConfigurer; public class EvaluationContextConfigurers { public static EvaluationContextConfigurer noopConfigurer() { diff --git a/src/main/java/pro/verron/docxstamper/preset/ExperimentalStampers.java b/src/main/java/pro/verron/officestamper/preset/ExperimentalStampers.java similarity index 92% rename from src/main/java/pro/verron/docxstamper/preset/ExperimentalStampers.java rename to src/main/java/pro/verron/officestamper/preset/ExperimentalStampers.java index c63ef3d3..7fd2357e 100644 --- a/src/main/java/pro/verron/docxstamper/preset/ExperimentalStampers.java +++ b/src/main/java/pro/verron/officestamper/preset/ExperimentalStampers.java @@ -1,8 +1,8 @@ -package pro.verron.docxstamper.preset; +package pro.verron.officestamper.preset; import org.docx4j.openpackaging.packages.PresentationMLPackage; import org.docx4j.openpackaging.packages.SpreadsheetMLPackage; -import pro.verron.docxstamper.api.OfficeStamper; +import pro.verron.officestamper.api.OfficeStamper; import pro.verron.officestamper.experimental.ExcelStamper; import pro.verron.officestamper.experimental.PowerpointStamper; diff --git a/src/main/java/pro/verron/docxstamper/preset/OfficeStamperConfigurations.java b/src/main/java/pro/verron/officestamper/preset/OfficeStamperConfigurations.java similarity index 91% rename from src/main/java/pro/verron/docxstamper/preset/OfficeStamperConfigurations.java rename to src/main/java/pro/verron/officestamper/preset/OfficeStamperConfigurations.java index 363bd487..36a5b1ea 100644 --- a/src/main/java/pro/verron/docxstamper/preset/OfficeStamperConfigurations.java +++ b/src/main/java/pro/verron/officestamper/preset/OfficeStamperConfigurations.java @@ -1,9 +1,9 @@ -package pro.verron.docxstamper.preset; +package pro.verron.officestamper.preset; import org.wickedsource.docxstamper.DocxStamperConfiguration; import org.wickedsource.docxstamper.preprocessor.MergeSameStyleRuns; import org.wickedsource.docxstamper.preprocessor.RemoveProofErrors; -import pro.verron.docxstamper.api.OfficeStamperConfiguration; +import pro.verron.officestamper.api.OfficeStamperConfiguration; /** * The OfficeStamperConfigurations class provides static methods diff --git a/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java b/src/main/java/pro/verron/officestamper/preset/OfficeStampers.java similarity index 89% rename from src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java rename to src/main/java/pro/verron/officestamper/preset/OfficeStampers.java index 24540692..4f524831 100644 --- a/src/main/java/pro/verron/docxstamper/preset/OfficeStampers.java +++ b/src/main/java/pro/verron/officestamper/preset/OfficeStampers.java @@ -1,11 +1,11 @@ -package pro.verron.docxstamper.preset; +package pro.verron.officestamper.preset; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.wickedsource.docxstamper.DocxStamper; import org.wickedsource.docxstamper.preprocessor.MergeSameStyleRuns; import org.wickedsource.docxstamper.preprocessor.RemoveProofErrors; -import pro.verron.docxstamper.api.OfficeStamper; -import pro.verron.docxstamper.api.OfficeStamperConfiguration; +import pro.verron.officestamper.api.OfficeStamper; +import pro.verron.officestamper.api.OfficeStamperConfiguration; /** * Main class of the docx-stamper library. diff --git a/src/main/java/pro/verron/docxstamper/preset/Resolvers.java b/src/main/java/pro/verron/officestamper/preset/Resolvers.java similarity index 96% rename from src/main/java/pro/verron/docxstamper/preset/Resolvers.java rename to src/main/java/pro/verron/officestamper/preset/Resolvers.java index 97e73523..a45108c1 100644 --- a/src/main/java/pro/verron/docxstamper/preset/Resolvers.java +++ b/src/main/java/pro/verron/officestamper/preset/Resolvers.java @@ -1,8 +1,8 @@ -package pro.verron.docxstamper.preset; +package pro.verron.officestamper.preset; -import pro.verron.docxstamper.api.Image; -import pro.verron.docxstamper.api.ObjectResolver; -import pro.verron.docxstamper.preset.resolver.*; +import pro.verron.officestamper.api.Image; +import pro.verron.officestamper.api.ObjectResolver; +import pro.verron.officestamper.preset.resolver.*; import java.time.LocalDate; import java.time.LocalDateTime; diff --git a/src/main/java/pro/verron/docxstamper/preset/package-info.java b/src/main/java/pro/verron/officestamper/preset/package-info.java similarity index 85% rename from src/main/java/pro/verron/docxstamper/preset/package-info.java rename to src/main/java/pro/verron/officestamper/preset/package-info.java index 2860c86c..557d2969 100644 --- a/src/main/java/pro/verron/docxstamper/preset/package-info.java +++ b/src/main/java/pro/verron/officestamper/preset/package-info.java @@ -4,6 +4,6 @@ * It should not be extended upon, put it can be used as a bootstrap for projects. */ @NonNullApi -package pro.verron.docxstamper.preset; +package pro.verron.officestamper.preset; import org.springframework.lang.NonNullApi; diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/DateResolver.java b/src/main/java/pro/verron/officestamper/preset/resolver/DateResolver.java similarity index 89% rename from src/main/java/pro/verron/docxstamper/preset/resolver/DateResolver.java rename to src/main/java/pro/verron/officestamper/preset/resolver/DateResolver.java index bd7f8f18..3842d809 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/DateResolver.java +++ b/src/main/java/pro/verron/officestamper/preset/resolver/DateResolver.java @@ -1,7 +1,7 @@ -package pro.verron.docxstamper.preset.resolver; +package pro.verron.officestamper.preset.resolver; -import pro.verron.docxstamper.api.ObjectResolver; -import pro.verron.docxstamper.api.StringResolver; +import pro.verron.officestamper.api.ObjectResolver; +import pro.verron.officestamper.api.StringResolver; import java.text.SimpleDateFormat; import java.time.ZoneId; diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java b/src/main/java/pro/verron/officestamper/preset/resolver/ImageResolver.java similarity index 91% rename from src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java rename to src/main/java/pro/verron/officestamper/preset/resolver/ImageResolver.java index 47b0732c..73375226 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/ImageResolver.java +++ b/src/main/java/pro/verron/officestamper/preset/resolver/ImageResolver.java @@ -1,12 +1,12 @@ -package pro.verron.docxstamper.preset.resolver; +package pro.verron.officestamper.preset.resolver; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.util.RunUtil; -import pro.verron.docxstamper.api.Image; -import pro.verron.docxstamper.api.ObjectResolver; -import pro.verron.docxstamper.api.OfficeStamperException; +import pro.verron.officestamper.api.Image; +import pro.verron.officestamper.api.ObjectResolver; +import pro.verron.officestamper.api.OfficeStamperException; import static org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage.createImagePart; diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateResolver.java b/src/main/java/pro/verron/officestamper/preset/resolver/LocalDateResolver.java similarity index 89% rename from src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateResolver.java rename to src/main/java/pro/verron/officestamper/preset/resolver/LocalDateResolver.java index 97dfe5bb..9861331b 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateResolver.java +++ b/src/main/java/pro/verron/officestamper/preset/resolver/LocalDateResolver.java @@ -1,6 +1,6 @@ -package pro.verron.docxstamper.preset.resolver; +package pro.verron.officestamper.preset.resolver; -import pro.verron.docxstamper.api.StringResolver; +import pro.verron.officestamper.api.StringResolver; import java.time.LocalDate; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateTimeResolver.java b/src/main/java/pro/verron/officestamper/preset/resolver/LocalDateTimeResolver.java similarity index 90% rename from src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateTimeResolver.java rename to src/main/java/pro/verron/officestamper/preset/resolver/LocalDateTimeResolver.java index 550feb63..b612159b 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/LocalDateTimeResolver.java +++ b/src/main/java/pro/verron/officestamper/preset/resolver/LocalDateTimeResolver.java @@ -1,6 +1,6 @@ -package pro.verron.docxstamper.preset.resolver; +package pro.verron.officestamper.preset.resolver; -import pro.verron.docxstamper.api.StringResolver; +import pro.verron.officestamper.api.StringResolver; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/LocalTimeResolver.java b/src/main/java/pro/verron/officestamper/preset/resolver/LocalTimeResolver.java similarity index 90% rename from src/main/java/pro/verron/docxstamper/preset/resolver/LocalTimeResolver.java rename to src/main/java/pro/verron/officestamper/preset/resolver/LocalTimeResolver.java index 4f3202e3..61361e0a 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/LocalTimeResolver.java +++ b/src/main/java/pro/verron/officestamper/preset/resolver/LocalTimeResolver.java @@ -1,6 +1,6 @@ -package pro.verron.docxstamper.preset.resolver; +package pro.verron.officestamper.preset.resolver; -import pro.verron.docxstamper.api.StringResolver; +import pro.verron.officestamper.api.StringResolver; import java.time.LocalTime; import java.time.format.DateTimeFormatter; diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/Null2DefaultResolver.java b/src/main/java/pro/verron/officestamper/preset/resolver/Null2DefaultResolver.java similarity index 92% rename from src/main/java/pro/verron/docxstamper/preset/resolver/Null2DefaultResolver.java rename to src/main/java/pro/verron/officestamper/preset/resolver/Null2DefaultResolver.java index 136a4d5f..fe5c028c 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/Null2DefaultResolver.java +++ b/src/main/java/pro/verron/officestamper/preset/resolver/Null2DefaultResolver.java @@ -1,9 +1,9 @@ -package pro.verron.docxstamper.preset.resolver; +package pro.verron.officestamper.preset.resolver; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; import org.wickedsource.docxstamper.util.RunUtil; -import pro.verron.docxstamper.api.ObjectResolver; +import pro.verron.officestamper.api.ObjectResolver; /** * The Null2DefaultResolver class is an implementation of the diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/Null2PlaceholderResolver.java b/src/main/java/pro/verron/officestamper/preset/resolver/Null2PlaceholderResolver.java similarity index 88% rename from src/main/java/pro/verron/docxstamper/preset/resolver/Null2PlaceholderResolver.java rename to src/main/java/pro/verron/officestamper/preset/resolver/Null2PlaceholderResolver.java index b2983db5..4fb137ba 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/Null2PlaceholderResolver.java +++ b/src/main/java/pro/verron/officestamper/preset/resolver/Null2PlaceholderResolver.java @@ -1,11 +1,11 @@ -package pro.verron.docxstamper.preset.resolver; +package pro.verron.officestamper.preset.resolver; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.util.RunUtil; -import pro.verron.docxstamper.api.ObjectResolver; -import pro.verron.docxstamper.api.Placeholder; +import pro.verron.officestamper.api.ObjectResolver; +import pro.verron.officestamper.api.Placeholder; /** * The {@link Null2PlaceholderResolver} class is an implementation of the ObjectResolver interface. diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/Resolvers.java b/src/main/java/pro/verron/officestamper/preset/resolver/Resolvers.java similarity index 61% rename from src/main/java/pro/verron/docxstamper/preset/resolver/Resolvers.java rename to src/main/java/pro/verron/officestamper/preset/resolver/Resolvers.java index 7172e30e..36b2f3ff 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/Resolvers.java +++ b/src/main/java/pro/verron/officestamper/preset/resolver/Resolvers.java @@ -1,13 +1,13 @@ -package pro.verron.docxstamper.preset.resolver; +package pro.verron.officestamper.preset.resolver; /** * @deprecated since 1.6.8, This class has been deprecated in the effort * of the library modularization. * It is recommended to use the - * {@link pro.verron.docxstamper.preset.Resolvers} class instead. + * {@link pro.verron.officestamper.preset.Resolvers} class instead. * This class will be removed in the future releases of the module. */ @Deprecated(since = "1.6.8", forRemoval = true) public class Resolvers - extends pro.verron.docxstamper.preset.Resolvers { + extends pro.verron.officestamper.preset.Resolvers { } diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/StringResolver.java b/src/main/java/pro/verron/officestamper/preset/resolver/StringResolver.java similarity index 73% rename from src/main/java/pro/verron/docxstamper/preset/resolver/StringResolver.java rename to src/main/java/pro/verron/officestamper/preset/resolver/StringResolver.java index a1631c99..4ad83e70 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/StringResolver.java +++ b/src/main/java/pro/verron/officestamper/preset/resolver/StringResolver.java @@ -1,15 +1,15 @@ -package pro.verron.docxstamper.preset.resolver; +package pro.verron.officestamper.preset.resolver; /** * @deprecated since 1.6.8, This class has been deprecated in the effort * of the library modularization. * It is recommended to use the - * {@link pro.verron.docxstamper.api.StringResolver} class instead. + * {@link pro.verron.officestamper.api.StringResolver} class instead. * This class will be removed in the future releases of the module. */ @Deprecated(since = "1.6.8", forRemoval = true) public abstract class StringResolver - extends pro.verron.docxstamper.api.StringResolver { + extends pro.verron.officestamper.api.StringResolver { /** * Creates a new StringResolver with the given type. * diff --git a/src/main/java/pro/verron/docxstamper/preset/resolver/ToStringResolver.java b/src/main/java/pro/verron/officestamper/preset/resolver/ToStringResolver.java similarity index 89% rename from src/main/java/pro/verron/docxstamper/preset/resolver/ToStringResolver.java rename to src/main/java/pro/verron/officestamper/preset/resolver/ToStringResolver.java index c3050867..090ce6fd 100644 --- a/src/main/java/pro/verron/docxstamper/preset/resolver/ToStringResolver.java +++ b/src/main/java/pro/verron/officestamper/preset/resolver/ToStringResolver.java @@ -1,9 +1,9 @@ -package pro.verron.docxstamper.preset.resolver; +package pro.verron.officestamper.preset.resolver; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; import org.wickedsource.docxstamper.util.RunUtil; -import pro.verron.docxstamper.api.ObjectResolver; +import pro.verron.officestamper.api.ObjectResolver; /** * This class is an implementation of the {@link ObjectResolver} interface diff --git a/src/test/java/module-info.java b/src/test/java/module-info.java index 18369369..5803e074 100644 --- a/src/test/java/module-info.java +++ b/src/test/java/module-info.java @@ -14,5 +14,7 @@ requires org.slf4j; requires jakarta.xml.bind; - opens pro.verron.docxstamper.test; + opens pro.verron.officestamper.test; + exports pro.verron.officestamper.test; + exports pro.verron.officestamper.api; } diff --git a/src/test/java/pro/verron/docxstamper/test/BasicExcelTest.java b/src/test/java/pro/verron/officestamper/test/BasicExcelTest.java similarity index 81% rename from src/test/java/pro/verron/docxstamper/test/BasicExcelTest.java rename to src/test/java/pro/verron/officestamper/test/BasicExcelTest.java index 41b3598f..1d101f57 100644 --- a/src/test/java/pro/verron/docxstamper/test/BasicExcelTest.java +++ b/src/test/java/pro/verron/officestamper/test/BasicExcelTest.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import org.docx4j.openpackaging.exceptions.Docx4JException; import org.junit.jupiter.api.Test; @@ -9,10 +9,10 @@ import static org.docx4j.openpackaging.packages.SpreadsheetMLPackage.load; import static org.junit.jupiter.api.Assertions.assertEquals; -import static pro.verron.docxstamper.preset.ExperimentalStampers.xlsxStamper; -import static pro.verron.docxstamper.test.IOStreams.getInputStream; -import static pro.verron.docxstamper.test.IOStreams.getOutputStream; -import static pro.verron.docxstamper.test.Stringifier.stringifyExcel; +import static pro.verron.officestamper.preset.ExperimentalStampers.xlsxStamper; +import static pro.verron.officestamper.test.IOStreams.getInputStream; +import static pro.verron.officestamper.test.IOStreams.getOutputStream; +import static pro.verron.officestamper.test.Stringifier.stringifyExcel; public class BasicExcelTest { @Test diff --git a/src/test/java/pro/verron/docxstamper/test/BasicPowerpointTest.java b/src/test/java/pro/verron/officestamper/test/BasicPowerpointTest.java similarity index 83% rename from src/test/java/pro/verron/docxstamper/test/BasicPowerpointTest.java rename to src/test/java/pro/verron/officestamper/test/BasicPowerpointTest.java index 5c406a28..23df22ba 100644 --- a/src/test/java/pro/verron/docxstamper/test/BasicPowerpointTest.java +++ b/src/test/java/pro/verron/officestamper/test/BasicPowerpointTest.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import org.docx4j.openpackaging.exceptions.Docx4JException; import org.docx4j.openpackaging.packages.PresentationMLPackage; @@ -11,9 +11,9 @@ import java.nio.file.Path; import static java.nio.file.Files.newInputStream; -import static pro.verron.docxstamper.preset.ExperimentalStampers.pptxStamper; -import static pro.verron.docxstamper.test.IOStreams.getInputStream; -import static pro.verron.docxstamper.test.IOStreams.getOutputStream; +import static pro.verron.officestamper.preset.ExperimentalStampers.pptxStamper; +import static pro.verron.officestamper.test.IOStreams.getInputStream; +import static pro.verron.officestamper.test.IOStreams.getOutputStream; public class BasicPowerpointTest { @Test diff --git a/src/test/java/pro/verron/docxstamper/test/BasicWordTest.java b/src/test/java/pro/verron/officestamper/test/BasicWordTest.java similarity index 85% rename from src/test/java/pro/verron/docxstamper/test/BasicWordTest.java rename to src/test/java/pro/verron/officestamper/test/BasicWordTest.java index d42e1685..27bf3e8c 100644 --- a/src/test/java/pro/verron/docxstamper/test/BasicWordTest.java +++ b/src/test/java/pro/verron/officestamper/test/BasicWordTest.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import org.junit.jupiter.api.Test; @@ -7,7 +7,7 @@ import java.nio.file.Path; import static org.junit.jupiter.api.Assertions.assertEquals; -import static pro.verron.docxstamper.preset.OfficeStamperConfigurations.standardWithPreprocessing; +import static pro.verron.officestamper.preset.OfficeStamperConfigurations.standardWithPreprocessing; public class BasicWordTest { @Test diff --git a/src/test/java/pro/verron/docxstamper/test/Contexts.java b/src/test/java/pro/verron/officestamper/test/Contexts.java similarity index 99% rename from src/test/java/pro/verron/docxstamper/test/Contexts.java rename to src/test/java/pro/verron/officestamper/test/Contexts.java index c9b0b676..b517d361 100644 --- a/src/test/java/pro/verron/docxstamper/test/Contexts.java +++ b/src/test/java/pro/verron/officestamper/test/Contexts.java @@ -1,7 +1,7 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; -import pro.verron.docxstamper.api.Image; -import pro.verron.docxstamper.api.StampTable; +import pro.verron.officestamper.api.Image; +import pro.verron.officestamper.api.StampTable; import java.util.*; diff --git a/src/test/java/pro/verron/docxstamper/test/CustomCommentProcessor.java b/src/test/java/pro/verron/officestamper/test/CustomCommentProcessor.java similarity index 90% rename from src/test/java/pro/verron/docxstamper/test/CustomCommentProcessor.java rename to src/test/java/pro/verron/officestamper/test/CustomCommentProcessor.java index 83e1ea80..a8dd88b2 100644 --- a/src/test/java/pro/verron/docxstamper/test/CustomCommentProcessor.java +++ b/src/test/java/pro/verron/officestamper/test/CustomCommentProcessor.java @@ -1,13 +1,13 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import org.docx4j.jaxb.Context; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.P; import org.docx4j.wml.R; -import pro.verron.docxstamper.api.AbstractCommentProcessor; -import pro.verron.docxstamper.api.Comment; -import pro.verron.docxstamper.api.CommentProcessor; -import pro.verron.docxstamper.api.ParagraphPlaceholderReplacer; +import pro.verron.officestamper.api.AbstractCommentProcessor; +import pro.verron.officestamper.api.Comment; +import pro.verron.officestamper.api.CommentProcessor; +import pro.verron.officestamper.api.ParagraphPlaceholderReplacer; import java.util.ArrayList; import java.util.List; diff --git a/src/test/java/pro/verron/docxstamper/test/CustomTypeResolver.java b/src/test/java/pro/verron/officestamper/test/CustomTypeResolver.java similarity index 87% rename from src/test/java/pro/verron/docxstamper/test/CustomTypeResolver.java rename to src/test/java/pro/verron/officestamper/test/CustomTypeResolver.java index f07425e1..92cc9855 100644 --- a/src/test/java/pro/verron/docxstamper/test/CustomTypeResolver.java +++ b/src/test/java/pro/verron/officestamper/test/CustomTypeResolver.java @@ -1,6 +1,6 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; -import pro.verron.docxstamper.api.StringResolver; +import pro.verron.officestamper.api.StringResolver; /** *

CustomTypeResolver class.

diff --git a/src/test/java/pro/verron/docxstamper/test/DefaultTests.java b/src/test/java/pro/verron/officestamper/test/DefaultTests.java similarity index 99% rename from src/test/java/pro/verron/docxstamper/test/DefaultTests.java rename to src/test/java/pro/verron/officestamper/test/DefaultTests.java index 8dd2c74e..280d68a3 100644 --- a/src/test/java/pro/verron/docxstamper/test/DefaultTests.java +++ b/src/test/java/pro/verron/officestamper/test/DefaultTests.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; @@ -6,11 +6,11 @@ import org.junit.jupiter.params.provider.MethodSource; import org.springframework.context.expression.MapAccessor; import org.springframework.expression.spel.SpelParserConfiguration; -import pro.verron.docxstamper.api.Image; -import pro.verron.docxstamper.api.OfficeStamperConfiguration; -import pro.verron.docxstamper.preset.EvaluationContextConfigurers; -import pro.verron.docxstamper.preset.OfficeStamperConfigurations; -import pro.verron.docxstamper.preset.Resolvers; +import pro.verron.officestamper.api.Image; +import pro.verron.officestamper.api.OfficeStamperConfiguration; +import pro.verron.officestamper.preset.EvaluationContextConfigurers; +import pro.verron.officestamper.preset.OfficeStamperConfigurations; +import pro.verron.officestamper.preset.Resolvers; import java.io.IOException; import java.io.InputStream; @@ -24,7 +24,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.junit.jupiter.params.provider.Arguments.of; -import static pro.verron.docxstamper.test.Contexts.*; +import static pro.verron.officestamper.test.Contexts.*; /** *

DefaultTests class.

diff --git a/src/test/java/pro/verron/docxstamper/test/DocxCollector.java b/src/test/java/pro/verron/officestamper/test/DocxCollector.java similarity index 96% rename from src/test/java/pro/verron/docxstamper/test/DocxCollector.java rename to src/test/java/pro/verron/officestamper/test/DocxCollector.java index 26e69780..6191c4c3 100644 --- a/src/test/java/pro/verron/docxstamper/test/DocxCollector.java +++ b/src/test/java/pro/verron/officestamper/test/DocxCollector.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import org.docx4j.TraversalUtil; diff --git a/src/test/java/pro/verron/docxstamper/test/FailOnUnresolvedPlaceholderTest.java b/src/test/java/pro/verron/officestamper/test/FailOnUnresolvedPlaceholderTest.java similarity index 85% rename from src/test/java/pro/verron/docxstamper/test/FailOnUnresolvedPlaceholderTest.java rename to src/test/java/pro/verron/officestamper/test/FailOnUnresolvedPlaceholderTest.java index a4584770..1f00ccfb 100644 --- a/src/test/java/pro/verron/docxstamper/test/FailOnUnresolvedPlaceholderTest.java +++ b/src/test/java/pro/verron/officestamper/test/FailOnUnresolvedPlaceholderTest.java @@ -1,8 +1,8 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.api.OfficeStamperException; -import pro.verron.docxstamper.preset.OfficeStamperConfigurations; +import pro.verron.officestamper.api.OfficeStamperException; +import pro.verron.officestamper.preset.OfficeStamperConfigurations; import java.io.IOException; import java.io.InputStream; @@ -10,7 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; -import static pro.verron.docxstamper.test.DefaultTests.getResource; +import static pro.verron.officestamper.test.DefaultTests.getResource; /** * @author Joseph Verron diff --git a/src/test/java/pro/verron/docxstamper/test/Functions.java b/src/test/java/pro/verron/officestamper/test/Functions.java similarity index 95% rename from src/test/java/pro/verron/docxstamper/test/Functions.java rename to src/test/java/pro/verron/officestamper/test/Functions.java index d33e98e3..90475f96 100644 --- a/src/test/java/pro/verron/docxstamper/test/Functions.java +++ b/src/test/java/pro/verron/officestamper/test/Functions.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; /** *

Functions class.

diff --git a/src/test/java/pro/verron/docxstamper/test/ICustomCommentProcessor.java b/src/test/java/pro/verron/officestamper/test/ICustomCommentProcessor.java similarity index 74% rename from src/test/java/pro/verron/docxstamper/test/ICustomCommentProcessor.java rename to src/test/java/pro/verron/officestamper/test/ICustomCommentProcessor.java index ec85c1af..a9c1593b 100644 --- a/src/test/java/pro/verron/docxstamper/test/ICustomCommentProcessor.java +++ b/src/test/java/pro/verron/officestamper/test/ICustomCommentProcessor.java @@ -1,6 +1,6 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; -import pro.verron.docxstamper.api.CommentProcessor; +import pro.verron.officestamper.api.CommentProcessor; /** *

ICustomCommentProcessor interface.

diff --git a/src/test/java/pro/verron/docxstamper/test/IOStreams.java b/src/test/java/pro/verron/officestamper/test/IOStreams.java similarity index 98% rename from src/test/java/pro/verron/docxstamper/test/IOStreams.java rename to src/test/java/pro/verron/officestamper/test/IOStreams.java index 57e2a9ae..3979ad44 100644 --- a/src/test/java/pro/verron/docxstamper/test/IOStreams.java +++ b/src/test/java/pro/verron/officestamper/test/IOStreams.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/test/java/pro/verron/docxstamper/test/MultiSectionTest.java b/src/test/java/pro/verron/officestamper/test/MultiSectionTest.java similarity index 83% rename from src/test/java/pro/verron/docxstamper/test/MultiSectionTest.java rename to src/test/java/pro/verron/officestamper/test/MultiSectionTest.java index 7789f255..c69c374f 100644 --- a/src/test/java/pro/verron/docxstamper/test/MultiSectionTest.java +++ b/src/test/java/pro/verron/officestamper/test/MultiSectionTest.java @@ -1,12 +1,12 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.preset.OfficeStamperConfigurations; +import pro.verron.officestamper.preset.OfficeStamperConfigurations; import java.nio.file.Path; import static org.junit.jupiter.api.Assertions.assertEquals; -import static pro.verron.docxstamper.test.DefaultTests.getResource; +import static pro.verron.officestamper.test.DefaultTests.getResource; /** * @author Joseph Verron diff --git a/src/test/java/pro/verron/docxstamper/test/MultiStampTest.java b/src/test/java/pro/verron/officestamper/test/MultiStampTest.java similarity index 91% rename from src/test/java/pro/verron/docxstamper/test/MultiStampTest.java rename to src/test/java/pro/verron/officestamper/test/MultiStampTest.java index a1ed4b86..1211cad4 100644 --- a/src/test/java/pro/verron/docxstamper/test/MultiStampTest.java +++ b/src/test/java/pro/verron/officestamper/test/MultiStampTest.java @@ -1,13 +1,13 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.preset.OfficeStamperConfigurations; +import pro.verron.officestamper.preset.OfficeStamperConfigurations; import java.nio.file.Path; import static org.junit.jupiter.api.Assertions.assertEquals; -import static pro.verron.docxstamper.test.Contexts.names; -import static pro.verron.docxstamper.test.DefaultTests.getResource; +import static pro.verron.officestamper.test.Contexts.names; +import static pro.verron.officestamper.test.DefaultTests.getResource; /** * @author Joseph Verron diff --git a/src/test/java/pro/verron/docxstamper/test/NullPointerResolutionTest.java b/src/test/java/pro/verron/officestamper/test/NullPointerResolutionTest.java similarity index 81% rename from src/test/java/pro/verron/docxstamper/test/NullPointerResolutionTest.java rename to src/test/java/pro/verron/officestamper/test/NullPointerResolutionTest.java index ef0c14be..6d5e6255 100644 --- a/src/test/java/pro/verron/docxstamper/test/NullPointerResolutionTest.java +++ b/src/test/java/pro/verron/officestamper/test/NullPointerResolutionTest.java @@ -1,15 +1,15 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.api.OfficeStamperException; -import pro.verron.docxstamper.preset.OfficeStamperConfigurations; +import pro.verron.officestamper.api.OfficeStamperException; +import pro.verron.officestamper.preset.OfficeStamperConfigurations; import java.io.IOException; import java.nio.file.Path; import java.util.List; import static org.junit.jupiter.api.Assertions.assertThrows; -import static pro.verron.docxstamper.test.DefaultTests.getResource; +import static pro.verron.officestamper.test.DefaultTests.getResource; /** * @author Joseph Verron diff --git a/src/test/java/pro/verron/docxstamper/test/NullishContext.java b/src/test/java/pro/verron/officestamper/test/NullishContext.java similarity index 98% rename from src/test/java/pro/verron/docxstamper/test/NullishContext.java rename to src/test/java/pro/verron/officestamper/test/NullishContext.java index 41392314..aa88751d 100644 --- a/src/test/java/pro/verron/docxstamper/test/NullishContext.java +++ b/src/test/java/pro/verron/officestamper/test/NullishContext.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import java.util.Objects; diff --git a/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java b/src/test/java/pro/verron/officestamper/test/PlaceholderReplacementInHeaderAndFooterTest.java similarity index 92% rename from src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java rename to src/test/java/pro/verron/officestamper/test/PlaceholderReplacementInHeaderAndFooterTest.java index e7641e4b..7663dc35 100644 --- a/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInHeaderAndFooterTest.java +++ b/src/test/java/pro/verron/officestamper/test/PlaceholderReplacementInHeaderAndFooterTest.java @@ -1,12 +1,12 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.preset.OfficeStamperConfigurations; +import pro.verron.officestamper.preset.OfficeStamperConfigurations; import java.nio.file.Path; import static org.junit.jupiter.api.Assertions.assertEquals; -import static pro.verron.docxstamper.test.DefaultTests.getResource; +import static pro.verron.officestamper.test.DefaultTests.getResource; /** * @author Joseph Verron diff --git a/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java b/src/test/java/pro/verron/officestamper/test/PlaceholderReplacementInTextBoxesTest.java similarity index 84% rename from src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java rename to src/test/java/pro/verron/officestamper/test/PlaceholderReplacementInTextBoxesTest.java index 5e040e86..b9aeea9d 100644 --- a/src/test/java/pro/verron/docxstamper/test/PlaceholderReplacementInTextBoxesTest.java +++ b/src/test/java/pro/verron/officestamper/test/PlaceholderReplacementInTextBoxesTest.java @@ -1,14 +1,14 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import org.docx4j.dml.wordprocessingDrawing.Anchor; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.preset.OfficeStamperConfigurations; +import pro.verron.officestamper.preset.OfficeStamperConfigurations; import java.nio.file.Path; import java.util.List; import static org.junit.jupiter.api.Assertions.assertIterableEquals; -import static pro.verron.docxstamper.test.DefaultTests.getResource; +import static pro.verron.officestamper.test.DefaultTests.getResource; /** * @author Joseph Verron diff --git a/src/test/java/pro/verron/docxstamper/test/RepeatDocPartBadPlaceholderTest.java b/src/test/java/pro/verron/officestamper/test/RepeatDocPartBadPlaceholderTest.java similarity index 85% rename from src/test/java/pro/verron/docxstamper/test/RepeatDocPartBadPlaceholderTest.java rename to src/test/java/pro/verron/officestamper/test/RepeatDocPartBadPlaceholderTest.java index e1e4ee43..bae9f624 100644 --- a/src/test/java/pro/verron/docxstamper/test/RepeatDocPartBadPlaceholderTest.java +++ b/src/test/java/pro/verron/officestamper/test/RepeatDocPartBadPlaceholderTest.java @@ -1,13 +1,13 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import pro.verron.docxstamper.api.OfficeStamperException; -import pro.verron.docxstamper.preset.OfficeStamperConfigurations; -import pro.verron.docxstamper.test.Contexts.Characters; -import pro.verron.docxstamper.test.Contexts.Role; +import pro.verron.officestamper.api.OfficeStamperException; +import pro.verron.officestamper.preset.OfficeStamperConfigurations; +import pro.verron.officestamper.test.Contexts.Characters; +import pro.verron.officestamper.test.Contexts.Role; import java.nio.file.Path; import java.util.Arrays; @@ -15,7 +15,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static pro.verron.docxstamper.test.DefaultTests.getResource; +import static pro.verron.officestamper.test.DefaultTests.getResource; /** * @author Jenei Attila diff --git a/src/test/java/pro/verron/docxstamper/test/RunCollector.java b/src/test/java/pro/verron/officestamper/test/RunCollector.java similarity index 94% rename from src/test/java/pro/verron/docxstamper/test/RunCollector.java rename to src/test/java/pro/verron/officestamper/test/RunCollector.java index 34843bb2..419b18e4 100644 --- a/src/test/java/pro/verron/docxstamper/test/RunCollector.java +++ b/src/test/java/pro/verron/officestamper/test/RunCollector.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import org.docx4j.utils.TraversalUtilVisitor; import org.docx4j.wml.R; diff --git a/src/test/java/pro/verron/docxstamper/test/SimpleGetter.java b/src/test/java/pro/verron/officestamper/test/SimpleGetter.java similarity index 97% rename from src/test/java/pro/verron/docxstamper/test/SimpleGetter.java rename to src/test/java/pro/verron/officestamper/test/SimpleGetter.java index 6edd1efe..c2229853 100644 --- a/src/test/java/pro/verron/docxstamper/test/SimpleGetter.java +++ b/src/test/java/pro/verron/officestamper/test/SimpleGetter.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import org.springframework.expression.EvaluationContext; import org.springframework.expression.PropertyAccessor; diff --git a/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java b/src/test/java/pro/verron/officestamper/test/SpelInjectionTest.java similarity index 78% rename from src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java rename to src/test/java/pro/verron/officestamper/test/SpelInjectionTest.java index 5292baaf..71996e41 100644 --- a/src/test/java/pro/verron/docxstamper/test/SpelInjectionTest.java +++ b/src/test/java/pro/verron/officestamper/test/SpelInjectionTest.java @@ -1,15 +1,15 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.api.OfficeStamperException; -import pro.verron.docxstamper.preset.OfficeStamperConfigurations; +import pro.verron.officestamper.api.OfficeStamperException; +import pro.verron.officestamper.preset.OfficeStamperConfigurations; import java.io.IOException; import java.nio.file.Path; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; -import static pro.verron.docxstamper.test.DefaultTests.getResource; +import static pro.verron.officestamper.test.DefaultTests.getResource; /** * @author Joseph Verron diff --git a/src/test/java/pro/verron/docxstamper/test/StampTableTest.java b/src/test/java/pro/verron/officestamper/test/StampTableTest.java similarity index 92% rename from src/test/java/pro/verron/docxstamper/test/StampTableTest.java rename to src/test/java/pro/verron/officestamper/test/StampTableTest.java index f2c3ffd5..ae17c700 100644 --- a/src/test/java/pro/verron/docxstamper/test/StampTableTest.java +++ b/src/test/java/pro/verron/officestamper/test/StampTableTest.java @@ -1,13 +1,13 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import org.junit.jupiter.api.Test; -import pro.verron.docxstamper.preset.OfficeStamperConfigurations; +import pro.verron.officestamper.preset.OfficeStamperConfigurations; import java.nio.file.Path; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; -import static pro.verron.docxstamper.test.DefaultTests.getResource; +import static pro.verron.officestamper.test.DefaultTests.getResource; /** * A test class that verifies that stampTable feature works correctly diff --git a/src/test/java/pro/verron/docxstamper/test/Stringifier.java b/src/test/java/pro/verron/officestamper/test/Stringifier.java similarity index 99% rename from src/test/java/pro/verron/docxstamper/test/Stringifier.java rename to src/test/java/pro/verron/officestamper/test/Stringifier.java index 1b5570f1..51f438b1 100644 --- a/src/test/java/pro/verron/docxstamper/test/Stringifier.java +++ b/src/test/java/pro/verron/officestamper/test/Stringifier.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import jakarta.xml.bind.JAXBElement; import org.docx4j.TextUtils; @@ -17,7 +17,7 @@ import org.docx4j.wml.Comments.Comment; import org.xlsx4j.org.apache.poi.ss.usermodel.DataFormatter; import org.xlsx4j.sml.Cell; -import pro.verron.docxstamper.api.OfficeStamperException; +import pro.verron.officestamper.api.OfficeStamperException; import pro.verron.officestamper.experimental.ExcelCollector; import pro.verron.officestamper.experimental.PowerpointCollector; import pro.verron.officestamper.experimental.PowerpointParagraph; diff --git a/src/test/java/pro/verron/docxstamper/test/SubContext.java b/src/test/java/pro/verron/officestamper/test/SubContext.java similarity index 98% rename from src/test/java/pro/verron/docxstamper/test/SubContext.java rename to src/test/java/pro/verron/officestamper/test/SubContext.java index 2cb8a960..6236eedc 100644 --- a/src/test/java/pro/verron/docxstamper/test/SubContext.java +++ b/src/test/java/pro/verron/officestamper/test/SubContext.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import java.util.List; import java.util.Objects; diff --git a/src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java b/src/test/java/pro/verron/officestamper/test/TestDocxStamper.java similarity index 96% rename from src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java rename to src/test/java/pro/verron/officestamper/test/TestDocxStamper.java index 81951f07..b4796d2d 100644 --- a/src/test/java/pro/verron/docxstamper/test/TestDocxStamper.java +++ b/src/test/java/pro/verron/officestamper/test/TestDocxStamper.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import org.docx4j.TraversalUtil; import org.docx4j.openpackaging.exceptions.Docx4JException; @@ -9,9 +9,9 @@ import org.docx4j.openpackaging.parts.relationships.RelationshipsPart; import org.docx4j.relationships.Relationship; import org.docx4j.wml.P; -import pro.verron.docxstamper.api.OfficeStamperConfiguration; -import pro.verron.docxstamper.api.StreamStamper; -import pro.verron.docxstamper.preset.OfficeStampers; +import pro.verron.officestamper.api.OfficeStamperConfiguration; +import pro.verron.officestamper.api.StreamStamper; +import pro.verron.officestamper.preset.OfficeStampers; import java.io.IOException; import java.io.InputStream; diff --git a/src/test/java/pro/verron/docxstamper/test/ThrowingSupplier.java b/src/test/java/pro/verron/officestamper/test/ThrowingSupplier.java similarity index 93% rename from src/test/java/pro/verron/docxstamper/test/ThrowingSupplier.java rename to src/test/java/pro/verron/officestamper/test/ThrowingSupplier.java index a3c22766..b72c539b 100644 --- a/src/test/java/pro/verron/docxstamper/test/ThrowingSupplier.java +++ b/src/test/java/pro/verron/officestamper/test/ThrowingSupplier.java @@ -1,4 +1,4 @@ -package pro.verron.docxstamper.test; +package pro.verron.officestamper.test; import java.util.function.Supplier; From b6bd79cacdd6717582c34b6105ef0a65f7014e1a Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 12 May 2024 22:52:42 +0800 Subject: [PATCH 118/134] Added Javadoc comments for improving code readability --- .../pro/verron/officestamper/api/Comment.java | 5 + .../api/EvaluationContextConfigurer.java | 6 + .../api/OfficeStamperConfiguration.java | 109 +++++++++++++++++- .../api/OfficeStamperException.java | 6 + .../verron/officestamper/api/Paragraph.java | 13 +++ .../officestamper/core/CommentUtil.java | 10 ++ .../officestamper/core/Placeholders.java | 14 +++ .../officestamper/core/StandardComment.java | 5 + .../experimental/ExcelCollector.java | 31 +++++ .../experimental/ExcelVisitor.java | 18 +++ .../experimental/PowerpointVisitor.java | 13 +++ .../preset/EvaluationContextConfigurers.java | 15 +++ .../preset/ExperimentalStampers.java | 11 +- 13 files changed, 253 insertions(+), 3 deletions(-) diff --git a/src/main/java/pro/verron/officestamper/api/Comment.java b/src/main/java/pro/verron/officestamper/api/Comment.java index bf709394..8477c81d 100644 --- a/src/main/java/pro/verron/officestamper/api/Comment.java +++ b/src/main/java/pro/verron/officestamper/api/Comment.java @@ -137,5 +137,10 @@ WordprocessingMLPackage getSubTemplate(WordprocessingMLPackage document) // TODO_LATER: Remove the setting method from interface to increase immutability void setComment(Comments.Comment comment); + /** + * Retrieves the WordprocessingMLPackage document. + * + * @return the WordprocessingMLPackage document. + */ WordprocessingMLPackage getDocument(); } diff --git a/src/main/java/pro/verron/officestamper/api/EvaluationContextConfigurer.java b/src/main/java/pro/verron/officestamper/api/EvaluationContextConfigurer.java index b279dee1..3377dc76 100644 --- a/src/main/java/pro/verron/officestamper/api/EvaluationContextConfigurer.java +++ b/src/main/java/pro/verron/officestamper/api/EvaluationContextConfigurer.java @@ -2,6 +2,12 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; +/** + * The EvaluationContextConfigurer interface allows for custom configuration of a Spring Expression Language + * (SPEL) EvaluationContext. + * Implementations of this interface can be used + * to add custom PropertyAccessors and MethodResolvers to the EvaluationContext. + */ public interface EvaluationContextConfigurer { /** * Configure the context before it's used by docxstamper. diff --git a/src/main/java/pro/verron/officestamper/api/OfficeStamperConfiguration.java b/src/main/java/pro/verron/officestamper/api/OfficeStamperConfiguration.java index 56d131a6..c8f726d4 100644 --- a/src/main/java/pro/verron/officestamper/api/OfficeStamperConfiguration.java +++ b/src/main/java/pro/verron/officestamper/api/OfficeStamperConfiguration.java @@ -15,7 +15,7 @@ public interface OfficeStamperConfiguration { /** * Retrieves the null replacement value. * - * @return an Optional containing the null replacement value, if it exists; otherwise, an empty Optional. + * @return an Optional containing the null replacement value if it exists; otherwise, an empty Optional. * * @deprecated since version 1.6.7 */ @@ -101,6 +101,7 @@ OfficeStamperConfiguration leaveEmptyOnExpressionError( * * @param resolvedType the Java class that the type resolver is responsible for. * @param resolver the implementation of {@link ITypeResolver} that resolves objects of the given type. + * @param type expected to be resolved by the resolver * * @return the updated OfficeStamperConfiguration object. * @@ -186,51 +187,157 @@ OfficeStamperConfiguration addCommentProcessor( */ String getLineBreakPlaceholder(); + /** + * Sets the line break placeholder used in the OfficeStamper configuration. + * + * @param lineBreakPlaceholder the line break placeholder as a String + * + * @return the updated OfficeStamperConfiguration object + */ OfficeStamperConfiguration setLineBreakPlaceholder( String lineBreakPlaceholder ); + /** + * Retrieves the EvaluationContextConfigurer for configuring the Spring Expression Language (SPEL) EvaluationContext + * used by the docxstamper. + * + * @return the EvaluationContextConfigurer for configuring the SPEL EvaluationContext. + */ EvaluationContextConfigurer getEvaluationContextConfigurer(); + /** + * Sets the EvaluationContextConfigurer for configuring the Spring Expression Language (SPEL) EvaluationContext. + * + * @param evaluationContextConfigurer the EvaluationContextConfigurer for configuring the SPEL EvaluationContext. + * Must implement the evaluateEvaluationContext() method. + * + * @return the updated OfficeStamperConfiguration object. + */ OfficeStamperConfiguration setEvaluationContextConfigurer( EvaluationContextConfigurer evaluationContextConfigurer ); + /** + * Retrieves the SpelParserConfiguration used by the OfficeStamperConfiguration. + * + * @return the SpelParserConfiguration object used by the OfficeStamperConfiguration. + */ SpelParserConfiguration getSpelParserConfiguration(); + /** + * Sets the SpelParserConfiguration used by the OfficeStamperConfiguration. + * + * @param spelParserConfiguration the SpelParserConfiguration to be set + * + * @return the updated OfficeStamperConfiguration object + */ OfficeStamperConfiguration setSpelParserConfiguration( SpelParserConfiguration spelParserConfiguration ); + /** + * Retrieves the map of expression functions associated with their corresponding classes. + * + * @return a map containing the expression functions as values and their corresponding classes as keys. + */ Map, Object> getExpressionFunctions(); + /** + * Retrieves a map of type resolvers. + * + * @return A map containing type resolvers. The keys of the map are classes and the values are instances + * of ITypeResolver. + * + * @deprecated This method has been deprecated since version 1.6.7 and will be removed in a future release. + * Use of this method is discouraged and should be replaced with an alternative implementation. + */ @Deprecated(since = "1.6.7", forRemoval = true) Map, ITypeResolver> getTypeResolvers(); + /** + * Retrieves the default type resolver. + * + * @return The default type resolver. + * + * @deprecated This method has been deprecated since version 1.6.7 and will be removed in a future release. + */ @Deprecated(since = "1.6.7", forRemoval = true) ITypeResolver getDefaultTypeResolver(); + /** + * Sets the default type resolver for the OfficeStamperConfiguration. + * This method is deprecated and will be removed in version 1.6.7. + * + * @param defaultResolver the default type resolver to be set + * + * @return the OfficeStamperConfiguration with the updated default type resolver + */ @Deprecated(since = "1.6.7", forRemoval = true) OfficeStamperConfiguration setDefaultTypeResolver( ITypeResolver defaultResolver ); + /** + * Returns a map of comment processors associated with their respective classes. + * + * @return The map of comment processors. The keys are the classes, and the values are the corresponding comment + * processors. + */ Map, Function> getCommentProcessors(); + /** + * Determines whether null values should be replaced. + * + * @return true if null values should be replaced, false otherwise. + * + * @deprecated Since version 1.6.7. This method will be removed in a future release. + */ @Deprecated(since = "1.6.7", forRemoval = true) boolean isReplaceNullValues(); + /** + * Retrieves the default value for null values. + * + * @return The default value for null values. + * + * @deprecated This method has been deprecated since version 1.6.7 and will be removed in a future release. + */ @Deprecated(since = "1.6.7", forRemoval = true) String getNullValuesDefault(); + /** + * Retrieves the list of pre-processors. + * + * @return The list of pre-processors. + */ List getPreprocessors(); + /** + * Retrieves the list of ObjectResolvers. + * + * @return The list of ObjectResolvers. + */ List getResolvers(); + /** + * Sets the list of object resolvers for the OfficeStamper configuration. + * + * @param resolvers the list of object resolvers to be set + * + * @return the updated OfficeStamperConfiguration instance + */ OfficeStamperConfiguration setResolvers( List resolvers ); + /** + * Adds an ObjectResolver to the OfficeStamperConfiguration. + * + * @param resolver The ObjectResolver to add to the configuration. + * + * @return The updated OfficeStamperConfiguration. + */ OfficeStamperConfiguration addResolver( ObjectResolver resolver ); diff --git a/src/main/java/pro/verron/officestamper/api/OfficeStamperException.java b/src/main/java/pro/verron/officestamper/api/OfficeStamperException.java index 525c8dd5..ddab13f0 100644 --- a/src/main/java/pro/verron/officestamper/api/OfficeStamperException.java +++ b/src/main/java/pro/verron/officestamper/api/OfficeStamperException.java @@ -42,6 +42,12 @@ public OfficeStamperException(String message, Throwable cause) { super(message, cause); } + /** + * OfficeStamperException is a subclass of RuntimeException + * that represents an exception + * that can be thrown during the processing of an Office document using the OfficeStamper + * library. + */ public OfficeStamperException() { super("Unexpected exception"); } diff --git a/src/main/java/pro/verron/officestamper/api/Paragraph.java b/src/main/java/pro/verron/officestamper/api/Paragraph.java index 65a891fe..1a6465ad 100644 --- a/src/main/java/pro/verron/officestamper/api/Paragraph.java +++ b/src/main/java/pro/verron/officestamper/api/Paragraph.java @@ -8,12 +8,25 @@ */ public interface Paragraph { + /** + * Replaces all occurrences of a placeholder with a specified replacement value within a paragraph. + * + * @param placeholder The placeholder to be replaced. + * @param replacement The replacement value for the placeholder. + */ default void replaceAll(Placeholder placeholder, R replacement) { while (contains(placeholder.expression())) { replace(placeholder, replacement); } } + /** + * Returns true if the given expression is found within the paragraph, otherwise returns false. + * + * @param expression The string to search for within the paragraph. + * + * @return true if the given expression is found within the paragraph, otherwise false. + */ default boolean contains(String expression) { return asString().contains(expression); } diff --git a/src/main/java/pro/verron/officestamper/core/CommentUtil.java b/src/main/java/pro/verron/officestamper/core/CommentUtil.java index e1404996..dfa4a0de 100644 --- a/src/main/java/pro/verron/officestamper/core/CommentUtil.java +++ b/src/main/java/pro/verron/officestamper/core/CommentUtil.java @@ -416,6 +416,16 @@ static void deleteCommentFromElements( deleteCommentFromElements(elements, commentId); } + /** + * Creates a sub Word document + * by extracting a specified comment and its associated content from the original document. + * + * @param comment The comment to be extracted from the original document. + * + * @return The sub Word document containing the content of the specified comment. + * + * @throws InvalidFormatException TODO_LATER: remove this explicit exception from public signature + */ public static WordprocessingMLPackage createSubWordDocument(Comment comment) throws InvalidFormatException { var elements = comment.getElements(); diff --git a/src/main/java/pro/verron/officestamper/core/Placeholders.java b/src/main/java/pro/verron/officestamper/core/Placeholders.java index 0f2b923e..01acc120 100644 --- a/src/main/java/pro/verron/officestamper/core/Placeholders.java +++ b/src/main/java/pro/verron/officestamper/core/Placeholders.java @@ -68,6 +68,13 @@ private Placeholders() { throw new DocxStamperException("Utility classes should not be instantiated!"); } + /** + * Finds variable expressions in a given paragraph. + * + * @param paragraph the paragraph in which to search for variable expressions + * + * @return a list of found variable expressions as {@link Placeholder} objects + */ public static List findVariables(Paragraph paragraph) { return findVariables(paragraph.asString()); } @@ -95,6 +102,13 @@ public static List findProcessors(String text) { return PROC_FINDER.find(text); } + /** + * Creates a new raw placeholder with the given text. + * + * @param text the text to be used as the content of the placeholder + * + * @return a new raw placeholder + */ public static Placeholder raw(String text) { return new StandardPlaceholder(RAW_MATCHER, text); } diff --git a/src/main/java/pro/verron/officestamper/core/StandardComment.java b/src/main/java/pro/verron/officestamper/core/StandardComment.java index ab69278c..414a0e69 100644 --- a/src/main/java/pro/verron/officestamper/core/StandardComment.java +++ b/src/main/java/pro/verron/officestamper/core/StandardComment.java @@ -33,6 +33,11 @@ public class StandardComment private CommentRangeEnd commentRangeEnd; private CommentReference commentReference; + /** + * Constructs a new StandardComment object. + * + * @param document the WordprocessingMLPackage document instance + */ public StandardComment(WordprocessingMLPackage document) { this.document = document; } diff --git a/src/main/java/pro/verron/officestamper/experimental/ExcelCollector.java b/src/main/java/pro/verron/officestamper/experimental/ExcelCollector.java index 7db64139..1a4afc81 100644 --- a/src/main/java/pro/verron/officestamper/experimental/ExcelCollector.java +++ b/src/main/java/pro/verron/officestamper/experimental/ExcelCollector.java @@ -3,6 +3,12 @@ import java.util.ArrayList; import java.util.List; +/** + * The ExcelCollector class is used to collect objects of a specific type from an Excel file. + * It extends the ExcelVisitor class and overrides the 'before' method to add the objects of the given type to a list. + * + * @param the type of objects to collect + */ public class ExcelCollector extends ExcelVisitor { private final Class aClass; @@ -10,8 +16,22 @@ public class ExcelCollector public ExcelCollector(Class aClass) { this.aClass = aClass; + /** + * Constructs a new ExcelCollector object with the given type. + * + * @param type the class representing the type of objects to collect + */ } + /** + * Collects objects of a specific type from an Excel file. + * + * @param the type of objects to collect + * @param object the Excel file or object to collect from + * @param type the class representing the type of objects to collect + * + * @return a List containing the collected objects + */ public static List collect( Object template, Class aClass @@ -21,10 +41,21 @@ public static List collect( return collector.collect(); } + /** + * Returns a List containing the collected objects. + * + * @return a List containing the collected objects + */ public List collect() { return list; } + /** + * This method is called before visiting an object in the ExcelCollector class. + * It checks if the object is an instance of the specified type and adds it to a list if it is. + * + * @param object the object being visited + */ @Override protected void before(Object object) { if (aClass.isInstance(object)) diff --git a/src/main/java/pro/verron/officestamper/experimental/ExcelVisitor.java b/src/main/java/pro/verron/officestamper/experimental/ExcelVisitor.java index f2c7fb05..5f777788 100644 --- a/src/main/java/pro/verron/officestamper/experimental/ExcelVisitor.java +++ b/src/main/java/pro/verron/officestamper/experimental/ExcelVisitor.java @@ -22,6 +22,12 @@ import java.util.Map.Entry; import java.util.Set; + +/** + * The ExcelVisitor class provides a mechanism for visiting different types of Excel objects. + * It contains visit methods for various types of objects and performs specific actions based on the object type. + * Subclasses can extend this class and override the before method to define custom behavior before visiting an object. + */ abstract class ExcelVisitor { private static final Logger logger = LoggerFactory.getLogger(ExcelVisitor.class); @@ -40,6 +46,11 @@ private static void ignore(@Nullable Object ignored1) { logger.trace("ignored visit of '{}' object", ignored1); } + /** + * Visits the given object and performs specific operations based on its type. + * + * @param object the object to visit + */ public final void visit(@Nullable Object object) { before(object); try { @@ -84,5 +95,12 @@ private void visit(Object... objs) { .forEach(this::visit); } + /** + * This method is called before performing a visit. + * It provides an opportunity to perform any necessary setup or validation + * before the actual visit takes place. + * + * @param object the object on which the visit will be performed. + */ protected abstract void before(@Nullable Object object); } diff --git a/src/main/java/pro/verron/officestamper/experimental/PowerpointVisitor.java b/src/main/java/pro/verron/officestamper/experimental/PowerpointVisitor.java index 565eaa69..018db2a8 100644 --- a/src/main/java/pro/verron/officestamper/experimental/PowerpointVisitor.java +++ b/src/main/java/pro/verron/officestamper/experimental/PowerpointVisitor.java @@ -37,6 +37,13 @@ private static void ignore(@Nullable Object ignored1) { logger.trace("ignored visit of '{}' object", ignored1); } + /** + * Signal the visited object through the before method, + * then apply logic to know the visit next elements or ignore deeper nesting + * based on the object type. + * + * @param object the object to visit + */ public final void visit(@Nullable Object object) { before(object); try { @@ -82,6 +89,12 @@ private void visit(Object... objs) { .forEach(this::visit); } + /** + * This abstract method is responsible for executing some tasks before a specific operation. + * It is intended to be implemented by subclasses. + * + * @param object The optional object that can be used as a parameter for the pre-operation tasks. + */ protected abstract void before(@Nullable Object object); } diff --git a/src/main/java/pro/verron/officestamper/preset/EvaluationContextConfigurers.java b/src/main/java/pro/verron/officestamper/preset/EvaluationContextConfigurers.java index eb602f86..3615ddcc 100644 --- a/src/main/java/pro/verron/officestamper/preset/EvaluationContextConfigurers.java +++ b/src/main/java/pro/verron/officestamper/preset/EvaluationContextConfigurers.java @@ -4,7 +4,22 @@ import org.wickedsource.docxstamper.el.NoOpEvaluationContextConfigurer; import pro.verron.officestamper.api.EvaluationContextConfigurer; +/** + * Utility class for configuring the EvaluationContext used by officestamper. + */ public class EvaluationContextConfigurers { + + /** + * Returns a {@link EvaluationContextConfigurer} instance that does no customization. + *

+ * This configurer does nothing to the StandardEvaluationContext class, and therefore all the + * unfiltered features are accessible. + * It should be used when there is a need to use the + * powerful features of the aforementioned class, and there is a trust that the template won't + * contain any dangerous injections. + * + * @return a {@link EvaluationContextConfigurer} instance + */ public static EvaluationContextConfigurer noopConfigurer() { return new NoOpEvaluationContextConfigurer(); } diff --git a/src/main/java/pro/verron/officestamper/preset/ExperimentalStampers.java b/src/main/java/pro/verron/officestamper/preset/ExperimentalStampers.java index 7fd2357e..03fe153f 100644 --- a/src/main/java/pro/verron/officestamper/preset/ExperimentalStampers.java +++ b/src/main/java/pro/verron/officestamper/preset/ExperimentalStampers.java @@ -6,13 +6,20 @@ import pro.verron.officestamper.experimental.ExcelStamper; import pro.verron.officestamper.experimental.PowerpointStamper; +/** + * ExperimentalStampers is a class that provides static methods for obtaining instances of OfficeStamper + * implementations for stamping PowerPoint presentations and Excel templates with context and writing + * the result to an OutputStream. + * + * @since 1.6.8 + */ public class ExperimentalStampers { /** * Returns a new instance of the OfficeStamper implementation - * for stamping Powerpoint presentations with context and writing + * for stamping PowerPoint presentations with context and writing * the result to an OutputStream. * - * @return a new OfficeStamper instance for Powerpoint presentations + * @return a new OfficeStamper instance for PowerPoint presentations * * @since 1.6.8 */ From a3b2246bc76b175d475de5997b7deef6556ea4b0 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 12 May 2024 22:53:32 +0800 Subject: [PATCH 119/134] Refactor ExcelCollector and ExcelVisitor classes The variable 'aClass' was renamed to 'type' for clearer readability in ExcelCollector class. Also, the variable 'template' was renamed to 'object' for consistency. In the ExcelVisitor class, the stream() method is now imported directly for more concise code. --- .../experimental/ExcelCollector.java | 19 ++++++++++--------- .../experimental/ExcelVisitor.java | 4 ++-- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/main/java/pro/verron/officestamper/experimental/ExcelCollector.java b/src/main/java/pro/verron/officestamper/experimental/ExcelCollector.java index 1a4afc81..65abd61c 100644 --- a/src/main/java/pro/verron/officestamper/experimental/ExcelCollector.java +++ b/src/main/java/pro/verron/officestamper/experimental/ExcelCollector.java @@ -11,16 +11,17 @@ */ public class ExcelCollector extends ExcelVisitor { - private final Class aClass; + + private final Class type; private final List list = new ArrayList<>(); - public ExcelCollector(Class aClass) { - this.aClass = aClass; /** * Constructs a new ExcelCollector object with the given type. * * @param type the class representing the type of objects to collect */ + public ExcelCollector(Class type) { + this.type = type; } /** @@ -33,11 +34,11 @@ public ExcelCollector(Class aClass) { * @return a List containing the collected objects */ public static List collect( - Object template, - Class aClass + Object object, + Class type ) { - var collector = new ExcelCollector<>(aClass); - collector.visit(template); + var collector = new ExcelCollector<>(type); + collector.visit(object); return collector.collect(); } @@ -58,7 +59,7 @@ public List collect() { */ @Override protected void before(Object object) { - if (aClass.isInstance(object)) - list.add(aClass.cast(object)); + if (type.isInstance(object)) + list.add(type.cast(object)); } } diff --git a/src/main/java/pro/verron/officestamper/experimental/ExcelVisitor.java b/src/main/java/pro/verron/officestamper/experimental/ExcelVisitor.java index 5f777788..04d752d0 100644 --- a/src/main/java/pro/verron/officestamper/experimental/ExcelVisitor.java +++ b/src/main/java/pro/verron/officestamper/experimental/ExcelVisitor.java @@ -22,6 +22,7 @@ import java.util.Map.Entry; import java.util.Set; +import static java.util.Arrays.stream; /** * The ExcelVisitor class provides a mechanism for visiting different types of Excel objects. @@ -91,8 +92,7 @@ public final void visit(@Nullable Object object) { } private void visit(Object... objs) { - Arrays.stream(objs) - .forEach(this::visit); + stream(objs).forEach(this::visit); } /** From e68213df05ac6c0466038a4468db70134b41ef0d Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 12 May 2024 22:53:48 +0800 Subject: [PATCH 120/134] Rename package from docxstamper to officestamper The package name has been changed from pro.verron.docxstamper.core to pro.verron.officestamper.core for consistent application level naming. This modification ensures the package's name accurately represents its functionalities. --- .../{docxstamper => officestamper}/core/package-info.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/main/java/pro/verron/{docxstamper => officestamper}/core/package-info.java (82%) diff --git a/src/main/java/pro/verron/docxstamper/core/package-info.java b/src/main/java/pro/verron/officestamper/core/package-info.java similarity index 82% rename from src/main/java/pro/verron/docxstamper/core/package-info.java rename to src/main/java/pro/verron/officestamper/core/package-info.java index 7119a709..a3ce818a 100644 --- a/src/main/java/pro/verron/docxstamper/core/package-info.java +++ b/src/main/java/pro/verron/officestamper/core/package-info.java @@ -4,6 +4,6 @@ * It should not be depended on by third-party. */ @NonNullApi -package pro.verron.docxstamper.core; +package pro.verron.officestamper.core; import org.springframework.lang.NonNullApi; From b3922c52e711f914c9c4342dd7e5bc2197b4d8cb Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 12 May 2024 22:54:12 +0800 Subject: [PATCH 121/134] Refactor PowerpointVisitor class for better readability This commit improves the readability of the PowerpointVisitor class. It replaces the use of Arrays.stream with static import to streamline code complexity. It also renames a variable for clarity and avoids redundancy. --- .../officestamper/experimental/PowerpointVisitor.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/pro/verron/officestamper/experimental/PowerpointVisitor.java b/src/main/java/pro/verron/officestamper/experimental/PowerpointVisitor.java index 018db2a8..f550b21d 100644 --- a/src/main/java/pro/verron/officestamper/experimental/PowerpointVisitor.java +++ b/src/main/java/pro/verron/officestamper/experimental/PowerpointVisitor.java @@ -14,11 +14,12 @@ import org.springframework.lang.Nullable; import pro.verron.officestamper.api.OfficeStamperException; -import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; +import static java.util.Arrays.stream; + abstract class PowerpointVisitor { private static final Logger logger = LoggerFactory.getLogger(PowerpointVisitor.class); @@ -33,8 +34,8 @@ private static void unexpectedVisit(Object object) { logger.debug(message); } - private static void ignore(@Nullable Object ignored1) { - logger.trace("ignored visit of '{}' object", ignored1); + private static void ignore(@Nullable Object ignored) { + logger.trace("ignored visit of '{}' object", ignored); } /** @@ -85,8 +86,7 @@ public final void visit(@Nullable Object object) { } private void visit(Object... objs) { - Arrays.stream(objs) - .forEach(this::visit); + stream(objs).forEach(this::visit); } /** From d2e1b1c8feec352329feaabacce1d1d8f7d1ccd4 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 12 May 2024 22:54:41 +0800 Subject: [PATCH 122/134] Add default EvaluationContextConfigurer for enhanced security A new function, defaultConfigurer(), has been added to provide a DefaultEvaluationContextConfigurer instance. This helps to instate better default security for EvaluationContext used by OfficeStamper. Enhanced security measures include limited property accessors, method resolvers, constructor resolvers, and more. --- .../preset/EvaluationContextConfigurers.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/java/pro/verron/officestamper/preset/EvaluationContextConfigurers.java b/src/main/java/pro/verron/officestamper/preset/EvaluationContextConfigurers.java index 3615ddcc..1f581972 100644 --- a/src/main/java/pro/verron/officestamper/preset/EvaluationContextConfigurers.java +++ b/src/main/java/pro/verron/officestamper/preset/EvaluationContextConfigurers.java @@ -1,6 +1,8 @@ package pro.verron.officestamper.preset; +import org.springframework.expression.EvaluationContext; +import org.wickedsource.docxstamper.el.DefaultEvaluationContextConfigurer; import org.wickedsource.docxstamper.el.NoOpEvaluationContextConfigurer; import pro.verron.officestamper.api.EvaluationContextConfigurer; @@ -23,4 +25,21 @@ public class EvaluationContextConfigurers { public static EvaluationContextConfigurer noopConfigurer() { return new NoOpEvaluationContextConfigurer(); } + + /** + * Returns a default {@link EvaluationContextConfigurer} instance. + *

+ * The default configurer provides better default security for the + * {@link EvaluationContext} used by office stamper. + * It sets up the context with enhanced security measures, such as + * limited property accessors, constructor resolvers, and method resolvers. + * It also sets a type locator, type converter, type comparator, and operator overloader. + * This configurer is recommended to be used when there is a need for improved security + * and protection against potential dangerous injections in the template. + * + * @return a {@link EvaluationContextConfigurer} instance with enhanced security features + */ + public static EvaluationContextConfigurer defaultConfigurer() { + return new DefaultEvaluationContextConfigurer(); + } } From 50124c0ce5a4d9bb05bb38eb15898a07b20537c8 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 12 May 2024 23:20:15 +0800 Subject: [PATCH 123/134] Refactor codebase to streamline package structure The package structure has been streamlined by relocating classes from the "resolver" sub-package to the "preset" package directly. Also, several classes were updated to reflect this movement and to ensure correct references. Furthermore, few deprecated classes were directly deleted as part of this refactoring. --- src/main/java/module-info.java | 7 ++++--- .../docxstamper/DocxStamperConfiguration.java | 2 +- .../docxstamper/processor/table/ITableResolver.java | 2 +- .../docxstamper/processor/table/StampTable.java | 4 ++-- .../docxstamper/processor/table/TableResolver.java | 2 +- .../replace/typeresolver/image/Image.java | 4 ++-- .../preset/{resolver => }/DateResolver.java | 2 +- .../verron/officestamper/{api => preset}/Image.java | 2 +- .../preset/{resolver => }/ImageResolver.java | 6 +++--- .../preset/{resolver => }/LocalDateResolver.java | 2 +- .../{resolver => }/LocalDateTimeResolver.java | 2 +- .../preset/{resolver => }/LocalTimeResolver.java | 2 +- .../preset/{resolver => }/Null2DefaultResolver.java | 5 +++-- .../{resolver => }/Null2PlaceholderResolver.java | 2 +- .../pro/verron/officestamper/preset/Resolvers.java | 2 -- .../officestamper/{api => preset}/StampTable.java | 2 +- .../preset/{resolver => }/StringResolver.java | 2 +- .../preset/{resolver => }/ToStringResolver.java | 2 +- .../officestamper/preset/resolver/Resolvers.java | 13 ------------- src/test/java/module-info.java | 3 +-- .../pro/verron/officestamper/test/Contexts.java | 4 ++-- .../pro/verron/officestamper/test/DefaultTests.java | 2 +- 22 files changed, 30 insertions(+), 44 deletions(-) rename src/main/java/pro/verron/officestamper/preset/{resolver => }/DateResolver.java (96%) rename src/main/java/pro/verron/officestamper/{api => preset}/Image.java (98%) rename src/main/java/pro/verron/officestamper/preset/{resolver => }/ImageResolver.java (94%) rename src/main/java/pro/verron/officestamper/preset/{resolver => }/LocalDateResolver.java (94%) rename src/main/java/pro/verron/officestamper/preset/{resolver => }/LocalDateTimeResolver.java (95%) rename src/main/java/pro/verron/officestamper/preset/{resolver => }/LocalTimeResolver.java (95%) rename src/main/java/pro/verron/officestamper/preset/{resolver => }/Null2DefaultResolver.java (89%) rename src/main/java/pro/verron/officestamper/preset/{resolver => }/Null2PlaceholderResolver.java (96%) rename src/main/java/pro/verron/officestamper/{api => preset}/StampTable.java (97%) rename src/main/java/pro/verron/officestamper/preset/{resolver => }/StringResolver.java (92%) rename src/main/java/pro/verron/officestamper/preset/{resolver => }/ToStringResolver.java (94%) delete mode 100644 src/main/java/pro/verron/officestamper/preset/resolver/Resolvers.java diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 46282cad..b388ba1a 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -45,14 +45,15 @@ requires static jakarta.xml.bind; opens pro.verron.officestamper.api; - opens pro.verron.officestamper.preset; - exports pro.verron.officestamper.api; + + opens pro.verron.officestamper.preset; exports pro.verron.officestamper.preset; - opens pro.verron.officestamper.core to pro.verron.officestamper.test; opens pro.verron.officestamper.experimental to pro.verron.officestamper.test; exports pro.verron.officestamper.experimental to pro.verron.officestamper.test; + + opens pro.verron.officestamper.core to pro.verron.officestamper.test; exports pro.verron.officestamper.core to pro.verron.officestamper.test; // TODO_LATER: remove all the following exports in next version diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java index bcf97f97..c571c65e 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java @@ -15,9 +15,9 @@ import org.wickedsource.docxstamper.replace.typeresolver.TypeResolver; import pro.verron.officestamper.api.*; import pro.verron.officestamper.preset.CommentProcessorFactory; +import pro.verron.officestamper.preset.Null2DefaultResolver; import pro.verron.officestamper.preset.OfficeStamperConfigurations; import pro.verron.officestamper.preset.Resolvers; -import pro.verron.officestamper.preset.resolver.Null2DefaultResolver; import java.util.*; import java.util.function.Function; diff --git a/src/main/java/org/wickedsource/docxstamper/processor/table/ITableResolver.java b/src/main/java/org/wickedsource/docxstamper/processor/table/ITableResolver.java index 57197147..1ebfd3b3 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/table/ITableResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/table/ITableResolver.java @@ -1,6 +1,6 @@ package org.wickedsource.docxstamper.processor.table; -import pro.verron.officestamper.api.StampTable; +import pro.verron.officestamper.preset.StampTable; /** * This interface is used to resolve a table in the template document. diff --git a/src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java b/src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java index 74ccdcd5..dd0b341e 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/table/StampTable.java @@ -8,12 +8,12 @@ * @deprecated since 1.6.8, This class has been deprecated in the effort * of the library modularization. * It is recommended to use the - * {@link pro.verron.officestamper.api.StampTable} class instead. + * {@link pro.verron.officestamper.preset.StampTable} class instead. * This class will be removed in the future releases of the module. */ @Deprecated(since = "1.6.8", forRemoval = true) public class StampTable - extends pro.verron.officestamper.api.StampTable { + extends pro.verron.officestamper.preset.StampTable { /** * {@inheritDoc} */ diff --git a/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java b/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java index dcd8ded1..df30f259 100644 --- a/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/processor/table/TableResolver.java @@ -9,8 +9,8 @@ import org.wickedsource.docxstamper.util.ParagraphUtil; import pro.verron.officestamper.api.CommentProcessor; import pro.verron.officestamper.api.ParagraphPlaceholderReplacer; -import pro.verron.officestamper.api.StampTable; import pro.verron.officestamper.core.PlaceholderReplacer; +import pro.verron.officestamper.preset.StampTable; import java.util.Collections; import java.util.HashMap; diff --git a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java index a2dfbfed..5af9e288 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/image/Image.java @@ -14,12 +14,12 @@ * @deprecated since 1.6.8, This class has been deprecated in the effort * of the library modularization. * It is recommended to use the - * {@link pro.verron.officestamper.api.Image} class instead. + * {@link pro.verron.officestamper.preset.Image} class instead. * This class will not be exported in the future releases of the module. */ @Deprecated(since = "1.6.8", forRemoval = true) public final class Image - extends pro.verron.officestamper.api.Image { + extends pro.verron.officestamper.preset.Image { /** *

Constructor for Image.

diff --git a/src/main/java/pro/verron/officestamper/preset/resolver/DateResolver.java b/src/main/java/pro/verron/officestamper/preset/DateResolver.java similarity index 96% rename from src/main/java/pro/verron/officestamper/preset/resolver/DateResolver.java rename to src/main/java/pro/verron/officestamper/preset/DateResolver.java index 3842d809..77b3ea2e 100644 --- a/src/main/java/pro/verron/officestamper/preset/resolver/DateResolver.java +++ b/src/main/java/pro/verron/officestamper/preset/DateResolver.java @@ -1,4 +1,4 @@ -package pro.verron.officestamper.preset.resolver; +package pro.verron.officestamper.preset; import pro.verron.officestamper.api.ObjectResolver; import pro.verron.officestamper.api.StringResolver; diff --git a/src/main/java/pro/verron/officestamper/api/Image.java b/src/main/java/pro/verron/officestamper/preset/Image.java similarity index 98% rename from src/main/java/pro/verron/officestamper/api/Image.java rename to src/main/java/pro/verron/officestamper/preset/Image.java index b990e69c..4e4fae2d 100644 --- a/src/main/java/pro/verron/officestamper/api/Image.java +++ b/src/main/java/pro/verron/officestamper/preset/Image.java @@ -1,4 +1,4 @@ -package pro.verron.officestamper.api; +package pro.verron.officestamper.preset; import org.apache.commons.io.IOUtils; diff --git a/src/main/java/pro/verron/officestamper/preset/resolver/ImageResolver.java b/src/main/java/pro/verron/officestamper/preset/ImageResolver.java similarity index 94% rename from src/main/java/pro/verron/officestamper/preset/resolver/ImageResolver.java rename to src/main/java/pro/verron/officestamper/preset/ImageResolver.java index 73375226..f6b31688 100644 --- a/src/main/java/pro/verron/officestamper/preset/resolver/ImageResolver.java +++ b/src/main/java/pro/verron/officestamper/preset/ImageResolver.java @@ -1,10 +1,10 @@ -package pro.verron.officestamper.preset.resolver; +package pro.verron.officestamper.preset; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; +import org.springframework.lang.Nullable; import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.util.RunUtil; -import pro.verron.officestamper.api.Image; import pro.verron.officestamper.api.ObjectResolver; import pro.verron.officestamper.api.OfficeStamperException; @@ -24,7 +24,7 @@ public class ImageResolver implements ObjectResolver { @Override - public boolean canResolve(Object object) { + public boolean canResolve(@Nullable Object object) { return object instanceof Image; } diff --git a/src/main/java/pro/verron/officestamper/preset/resolver/LocalDateResolver.java b/src/main/java/pro/verron/officestamper/preset/LocalDateResolver.java similarity index 94% rename from src/main/java/pro/verron/officestamper/preset/resolver/LocalDateResolver.java rename to src/main/java/pro/verron/officestamper/preset/LocalDateResolver.java index 9861331b..ee455629 100644 --- a/src/main/java/pro/verron/officestamper/preset/resolver/LocalDateResolver.java +++ b/src/main/java/pro/verron/officestamper/preset/LocalDateResolver.java @@ -1,4 +1,4 @@ -package pro.verron.officestamper.preset.resolver; +package pro.verron.officestamper.preset; import pro.verron.officestamper.api.StringResolver; diff --git a/src/main/java/pro/verron/officestamper/preset/resolver/LocalDateTimeResolver.java b/src/main/java/pro/verron/officestamper/preset/LocalDateTimeResolver.java similarity index 95% rename from src/main/java/pro/verron/officestamper/preset/resolver/LocalDateTimeResolver.java rename to src/main/java/pro/verron/officestamper/preset/LocalDateTimeResolver.java index b612159b..02e746d6 100644 --- a/src/main/java/pro/verron/officestamper/preset/resolver/LocalDateTimeResolver.java +++ b/src/main/java/pro/verron/officestamper/preset/LocalDateTimeResolver.java @@ -1,4 +1,4 @@ -package pro.verron.officestamper.preset.resolver; +package pro.verron.officestamper.preset; import pro.verron.officestamper.api.StringResolver; diff --git a/src/main/java/pro/verron/officestamper/preset/resolver/LocalTimeResolver.java b/src/main/java/pro/verron/officestamper/preset/LocalTimeResolver.java similarity index 95% rename from src/main/java/pro/verron/officestamper/preset/resolver/LocalTimeResolver.java rename to src/main/java/pro/verron/officestamper/preset/LocalTimeResolver.java index 61361e0a..ff8fd592 100644 --- a/src/main/java/pro/verron/officestamper/preset/resolver/LocalTimeResolver.java +++ b/src/main/java/pro/verron/officestamper/preset/LocalTimeResolver.java @@ -1,4 +1,4 @@ -package pro.verron.officestamper.preset.resolver; +package pro.verron.officestamper.preset; import pro.verron.officestamper.api.StringResolver; diff --git a/src/main/java/pro/verron/officestamper/preset/resolver/Null2DefaultResolver.java b/src/main/java/pro/verron/officestamper/preset/Null2DefaultResolver.java similarity index 89% rename from src/main/java/pro/verron/officestamper/preset/resolver/Null2DefaultResolver.java rename to src/main/java/pro/verron/officestamper/preset/Null2DefaultResolver.java index fe5c028c..04065a25 100644 --- a/src/main/java/pro/verron/officestamper/preset/resolver/Null2DefaultResolver.java +++ b/src/main/java/pro/verron/officestamper/preset/Null2DefaultResolver.java @@ -1,7 +1,8 @@ -package pro.verron.officestamper.preset.resolver; +package pro.verron.officestamper.preset; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; +import org.springframework.lang.Nullable; import org.wickedsource.docxstamper.util.RunUtil; import pro.verron.officestamper.api.ObjectResolver; @@ -29,7 +30,7 @@ public Null2DefaultResolver(String text) { } @Override - public boolean canResolve(Object object) { + public boolean canResolve(@Nullable Object object) { return object == null; } diff --git a/src/main/java/pro/verron/officestamper/preset/resolver/Null2PlaceholderResolver.java b/src/main/java/pro/verron/officestamper/preset/Null2PlaceholderResolver.java similarity index 96% rename from src/main/java/pro/verron/officestamper/preset/resolver/Null2PlaceholderResolver.java rename to src/main/java/pro/verron/officestamper/preset/Null2PlaceholderResolver.java index 4fb137ba..3ad15e4b 100644 --- a/src/main/java/pro/verron/officestamper/preset/resolver/Null2PlaceholderResolver.java +++ b/src/main/java/pro/verron/officestamper/preset/Null2PlaceholderResolver.java @@ -1,4 +1,4 @@ -package pro.verron.officestamper.preset.resolver; +package pro.verron.officestamper.preset; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; diff --git a/src/main/java/pro/verron/officestamper/preset/Resolvers.java b/src/main/java/pro/verron/officestamper/preset/Resolvers.java index a45108c1..92d2dc99 100644 --- a/src/main/java/pro/verron/officestamper/preset/Resolvers.java +++ b/src/main/java/pro/verron/officestamper/preset/Resolvers.java @@ -1,8 +1,6 @@ package pro.verron.officestamper.preset; -import pro.verron.officestamper.api.Image; import pro.verron.officestamper.api.ObjectResolver; -import pro.verron.officestamper.preset.resolver.*; import java.time.LocalDate; import java.time.LocalDateTime; diff --git a/src/main/java/pro/verron/officestamper/api/StampTable.java b/src/main/java/pro/verron/officestamper/preset/StampTable.java similarity index 97% rename from src/main/java/pro/verron/officestamper/api/StampTable.java rename to src/main/java/pro/verron/officestamper/preset/StampTable.java index 2c17a982..eea54f82 100644 --- a/src/main/java/pro/verron/officestamper/api/StampTable.java +++ b/src/main/java/pro/verron/officestamper/preset/StampTable.java @@ -1,4 +1,4 @@ -package pro.verron.officestamper.api; +package pro.verron.officestamper.preset; import org.springframework.lang.NonNull; diff --git a/src/main/java/pro/verron/officestamper/preset/resolver/StringResolver.java b/src/main/java/pro/verron/officestamper/preset/StringResolver.java similarity index 92% rename from src/main/java/pro/verron/officestamper/preset/resolver/StringResolver.java rename to src/main/java/pro/verron/officestamper/preset/StringResolver.java index 4ad83e70..32332345 100644 --- a/src/main/java/pro/verron/officestamper/preset/resolver/StringResolver.java +++ b/src/main/java/pro/verron/officestamper/preset/StringResolver.java @@ -1,4 +1,4 @@ -package pro.verron.officestamper.preset.resolver; +package pro.verron.officestamper.preset; /** * @deprecated since 1.6.8, This class has been deprecated in the effort diff --git a/src/main/java/pro/verron/officestamper/preset/resolver/ToStringResolver.java b/src/main/java/pro/verron/officestamper/preset/ToStringResolver.java similarity index 94% rename from src/main/java/pro/verron/officestamper/preset/resolver/ToStringResolver.java rename to src/main/java/pro/verron/officestamper/preset/ToStringResolver.java index 090ce6fd..841ed241 100644 --- a/src/main/java/pro/verron/officestamper/preset/resolver/ToStringResolver.java +++ b/src/main/java/pro/verron/officestamper/preset/ToStringResolver.java @@ -1,4 +1,4 @@ -package pro.verron.officestamper.preset.resolver; +package pro.verron.officestamper.preset; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; diff --git a/src/main/java/pro/verron/officestamper/preset/resolver/Resolvers.java b/src/main/java/pro/verron/officestamper/preset/resolver/Resolvers.java deleted file mode 100644 index 36b2f3ff..00000000 --- a/src/main/java/pro/verron/officestamper/preset/resolver/Resolvers.java +++ /dev/null @@ -1,13 +0,0 @@ -package pro.verron.officestamper.preset.resolver; - -/** - * @deprecated since 1.6.8, This class has been deprecated in the effort - * of the library modularization. - * It is recommended to use the - * {@link pro.verron.officestamper.preset.Resolvers} class instead. - * This class will be removed in the future releases of the module. - */ -@Deprecated(since = "1.6.8", forRemoval = true) -public class Resolvers - extends pro.verron.officestamper.preset.Resolvers { -} diff --git a/src/test/java/module-info.java b/src/test/java/module-info.java index 5803e074..b9968ea6 100644 --- a/src/test/java/module-info.java +++ b/src/test/java/module-info.java @@ -1,5 +1,5 @@ module pro.verron.officestamper.test { - requires transitive pro.verron.officestamper; + requires pro.verron.officestamper; requires org.junit.jupiter.api; requires org.junit.jupiter.params; @@ -16,5 +16,4 @@ opens pro.verron.officestamper.test; exports pro.verron.officestamper.test; - exports pro.verron.officestamper.api; } diff --git a/src/test/java/pro/verron/officestamper/test/Contexts.java b/src/test/java/pro/verron/officestamper/test/Contexts.java index b517d361..51dd3556 100644 --- a/src/test/java/pro/verron/officestamper/test/Contexts.java +++ b/src/test/java/pro/verron/officestamper/test/Contexts.java @@ -1,7 +1,7 @@ package pro.verron.officestamper.test; -import pro.verron.officestamper.api.Image; -import pro.verron.officestamper.api.StampTable; +import pro.verron.officestamper.preset.Image; +import pro.verron.officestamper.preset.StampTable; import java.util.*; diff --git a/src/test/java/pro/verron/officestamper/test/DefaultTests.java b/src/test/java/pro/verron/officestamper/test/DefaultTests.java index 280d68a3..3952c72a 100644 --- a/src/test/java/pro/verron/officestamper/test/DefaultTests.java +++ b/src/test/java/pro/verron/officestamper/test/DefaultTests.java @@ -6,9 +6,9 @@ import org.junit.jupiter.params.provider.MethodSource; import org.springframework.context.expression.MapAccessor; import org.springframework.expression.spel.SpelParserConfiguration; -import pro.verron.officestamper.api.Image; import pro.verron.officestamper.api.OfficeStamperConfiguration; import pro.verron.officestamper.preset.EvaluationContextConfigurers; +import pro.verron.officestamper.preset.Image; import pro.verron.officestamper.preset.OfficeStamperConfigurations; import pro.verron.officestamper.preset.Resolvers; From 80607d4693fba6d9caed773b2bb608c6cecbf693 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 12 May 2024 23:22:29 +0800 Subject: [PATCH 124/134] Add @Nullable annotation to canResolve method parameters All instances of the canResolve method across multiple classes have been updated to take a @Nullable Object. This ensures better code safety by explicitly allowing the parameter to be null, improving error handling and preventative programming measures. --- .../replace/typeresolver/LegacyFallbackResolver.java | 3 ++- .../docxstamper/replace/typeresolver/TypeResolver.java | 3 ++- src/main/java/pro/verron/officestamper/api/ObjectResolver.java | 3 ++- src/main/java/pro/verron/officestamper/api/StringResolver.java | 3 ++- .../verron/officestamper/preset/Null2PlaceholderResolver.java | 3 ++- .../java/pro/verron/officestamper/preset/ToStringResolver.java | 3 ++- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/LegacyFallbackResolver.java b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/LegacyFallbackResolver.java index 6097e288..922f6f49 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/LegacyFallbackResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/LegacyFallbackResolver.java @@ -2,6 +2,7 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; +import org.springframework.lang.Nullable; import org.wickedsource.docxstamper.util.RunUtil; import pro.verron.officestamper.api.ObjectResolver; import pro.verron.officestamper.preset.Resolvers; @@ -30,7 +31,7 @@ private static String format(Object object) { } @Override - public boolean canResolve(Object object) { + public boolean canResolve(@Nullable Object object) { return true; } diff --git a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/TypeResolver.java b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/TypeResolver.java index 5105c4b7..84931228 100644 --- a/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/TypeResolver.java +++ b/src/main/java/org/wickedsource/docxstamper/replace/typeresolver/TypeResolver.java @@ -2,6 +2,7 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; +import org.springframework.lang.Nullable; import org.wickedsource.docxstamper.api.DocxStamperException; import pro.verron.officestamper.api.ITypeResolver; import pro.verron.officestamper.api.ObjectResolver; @@ -49,7 +50,7 @@ public TypeResolver( * @return true if the object can be resolved, false otherwise */ @Override - public boolean canResolve(Object object) { + public boolean canResolve(@Nullable Object object) { if (object == null && this.nullProof) return true; return type.isInstance(object); diff --git a/src/main/java/pro/verron/officestamper/api/ObjectResolver.java b/src/main/java/pro/verron/officestamper/api/ObjectResolver.java index c096090c..92bf7ac6 100644 --- a/src/main/java/pro/verron/officestamper/api/ObjectResolver.java +++ b/src/main/java/pro/verron/officestamper/api/ObjectResolver.java @@ -3,6 +3,7 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; import org.slf4j.LoggerFactory; +import org.springframework.lang.Nullable; import org.wickedsource.docxstamper.api.DocxStamperException; /** @@ -47,7 +48,7 @@ default R resolve( * * @return true if the object can be resolved, false otherwise */ - boolean canResolve(Object object); + boolean canResolve(@Nullable Object object); /** * Resolves the expression in the given document with the provided object. diff --git a/src/main/java/pro/verron/officestamper/api/StringResolver.java b/src/main/java/pro/verron/officestamper/api/StringResolver.java index f82173a0..41b9553e 100644 --- a/src/main/java/pro/verron/officestamper/api/StringResolver.java +++ b/src/main/java/pro/verron/officestamper/api/StringResolver.java @@ -2,6 +2,7 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; +import org.springframework.lang.Nullable; import org.wickedsource.docxstamper.util.RunUtil; /** @@ -37,7 +38,7 @@ protected StringResolver(Class type) { * @return true if the object can be resolved, false otherwise */ @Override - public final boolean canResolve(Object object) { + public final boolean canResolve(@Nullable Object object) { return type.isInstance(object); } diff --git a/src/main/java/pro/verron/officestamper/preset/Null2PlaceholderResolver.java b/src/main/java/pro/verron/officestamper/preset/Null2PlaceholderResolver.java index 3ad15e4b..57c7178f 100644 --- a/src/main/java/pro/verron/officestamper/preset/Null2PlaceholderResolver.java +++ b/src/main/java/pro/verron/officestamper/preset/Null2PlaceholderResolver.java @@ -2,6 +2,7 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; +import org.springframework.lang.Nullable; import org.wickedsource.docxstamper.api.DocxStamperException; import org.wickedsource.docxstamper.util.RunUtil; import pro.verron.officestamper.api.ObjectResolver; @@ -24,7 +25,7 @@ public Null2PlaceholderResolver() { } @Override - public boolean canResolve(Object object) { + public boolean canResolve(@Nullable Object object) { return object == null; } diff --git a/src/main/java/pro/verron/officestamper/preset/ToStringResolver.java b/src/main/java/pro/verron/officestamper/preset/ToStringResolver.java index 841ed241..1e63ecf7 100644 --- a/src/main/java/pro/verron/officestamper/preset/ToStringResolver.java +++ b/src/main/java/pro/verron/officestamper/preset/ToStringResolver.java @@ -2,6 +2,7 @@ import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.wml.R; +import org.springframework.lang.Nullable; import org.wickedsource.docxstamper.util.RunUtil; import pro.verron.officestamper.api.ObjectResolver; @@ -17,7 +18,7 @@ public class ToStringResolver implements ObjectResolver { @Override - public boolean canResolve(Object object) { + public boolean canResolve(@Nullable Object object) { return object != null; } From 0457953e360705065b63c2d078d73d5d9ccd9034 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Sun, 12 May 2024 23:31:08 +0800 Subject: [PATCH 125/134] Refactor ObjectResolvers into the Resolvers file All the ObjectResolvers individual classes have been moved from the preset folder into the Resolvers file for better maintenance and management. The classes include DateResolver, ImageResolver, LocalDateResolver, LocalDateTimeResolver, LocalTimeResolver, Null2DefaultResolver, Null2PlaceholderResolver, and ToStringResolver. --- .../docxstamper/DocxStamperConfiguration.java | 7 +- .../officestamper/preset/DateResolver.java | 56 --- .../officestamper/preset/ImageResolver.java | 63 ---- .../preset/LocalDateResolver.java | 41 --- .../preset/LocalDateTimeResolver.java | 42 --- .../preset/LocalTimeResolver.java | 41 --- .../preset/Null2DefaultResolver.java | 54 --- .../preset/Null2PlaceholderResolver.java | 49 --- .../officestamper/preset/Resolvers.java | 335 ++++++++++++++++++ .../officestamper/preset/StringResolver.java | 21 -- .../preset/ToStringResolver.java | 33 -- 11 files changed, 338 insertions(+), 404 deletions(-) delete mode 100644 src/main/java/pro/verron/officestamper/preset/DateResolver.java delete mode 100644 src/main/java/pro/verron/officestamper/preset/ImageResolver.java delete mode 100644 src/main/java/pro/verron/officestamper/preset/LocalDateResolver.java delete mode 100644 src/main/java/pro/verron/officestamper/preset/LocalDateTimeResolver.java delete mode 100644 src/main/java/pro/verron/officestamper/preset/LocalTimeResolver.java delete mode 100644 src/main/java/pro/verron/officestamper/preset/Null2DefaultResolver.java delete mode 100644 src/main/java/pro/verron/officestamper/preset/Null2PlaceholderResolver.java delete mode 100644 src/main/java/pro/verron/officestamper/preset/StringResolver.java delete mode 100644 src/main/java/pro/verron/officestamper/preset/ToStringResolver.java diff --git a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java index c571c65e..178405f3 100644 --- a/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java +++ b/src/main/java/org/wickedsource/docxstamper/DocxStamperConfiguration.java @@ -15,7 +15,6 @@ import org.wickedsource.docxstamper.replace.typeresolver.TypeResolver; import pro.verron.officestamper.api.*; import pro.verron.officestamper.preset.CommentProcessorFactory; -import pro.verron.officestamper.preset.Null2DefaultResolver; import pro.verron.officestamper.preset.OfficeStamperConfigurations; import pro.verron.officestamper.preset.Resolvers; @@ -97,9 +96,9 @@ public DocxStamperConfiguration() { @Deprecated(since = "1.6.7") public Optional nullReplacementValue() { return resolvers.stream() - .filter(Null2DefaultResolver.class::isInstance) - .map(Null2DefaultResolver.class::cast) - .map(Null2DefaultResolver::defaultValue) + .filter(Resolvers.Null2DefaultResolver.class::isInstance) + .map(Resolvers.Null2DefaultResolver.class::cast) + .map(Resolvers.Null2DefaultResolver::defaultValue) .findFirst(); } diff --git a/src/main/java/pro/verron/officestamper/preset/DateResolver.java b/src/main/java/pro/verron/officestamper/preset/DateResolver.java deleted file mode 100644 index 77b3ea2e..00000000 --- a/src/main/java/pro/verron/officestamper/preset/DateResolver.java +++ /dev/null @@ -1,56 +0,0 @@ -package pro.verron.officestamper.preset; - -import pro.verron.officestamper.api.ObjectResolver; -import pro.verron.officestamper.api.StringResolver; - -import java.text.SimpleDateFormat; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; -import java.util.Date; - -/** - * This {@link ObjectResolver} creates a formatted date {@link String} for - * expressions that return a {@link Date} object. - * - * @author Joseph Verron - * @version ${version} - * @since 1.6.7 - */ -public final class DateResolver - extends StringResolver { - - private final DateTimeFormatter formatter; - - /** - * Creates a new DateResolver that uses the format "dd.MM.yyyy". - */ - public DateResolver() { - this(DateTimeFormatter.ofPattern("dd.MM.yyyy")); - } - - /** - * Creates a new DateResolver. - * - * @param formatter the format to use for date formatting. See - * {@link SimpleDateFormat}. - */ - public DateResolver(DateTimeFormatter formatter) { - super(Date.class); - this.formatter = formatter; - } - - /** - * Resolves a formatted date string for the given {@link Date} object. - * - * @param date the {@link Date} object to be resolved. - * @return the formatted date string. - */ - @Override - protected String resolve(Date date) { - var zone = ZoneId.systemDefault(); - var localDate = date.toInstant() - .atZone(zone) - .toLocalDate(); - return formatter.format(localDate); - } -} diff --git a/src/main/java/pro/verron/officestamper/preset/ImageResolver.java b/src/main/java/pro/verron/officestamper/preset/ImageResolver.java deleted file mode 100644 index f6b31688..00000000 --- a/src/main/java/pro/verron/officestamper/preset/ImageResolver.java +++ /dev/null @@ -1,63 +0,0 @@ -package pro.verron.officestamper.preset; - -import org.docx4j.openpackaging.packages.WordprocessingMLPackage; -import org.docx4j.wml.R; -import org.springframework.lang.Nullable; -import org.wickedsource.docxstamper.api.DocxStamperException; -import org.wickedsource.docxstamper.util.RunUtil; -import pro.verron.officestamper.api.ObjectResolver; -import pro.verron.officestamper.api.OfficeStamperException; - -import static org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage.createImagePart; - -/** - * This {@link ObjectResolver} allows context objects to return objects of - * type {@link Image}. An expression that resolves to an {@link Image} - * object will be replaced by an actual image in the resulting .docx document. - * The image will be put as an inline into the surrounding paragraph of text. - * - * @author Joseph Verron - * @version ${version} - * @since 1.6.7 - */ -public class ImageResolver - implements ObjectResolver { - - @Override - public boolean canResolve(@Nullable Object object) { - return object instanceof Image; - } - - @Override - public R resolve( - WordprocessingMLPackage document, - String expression, - Object object - ) { - if (object instanceof Image image) - return resolve(document, image); - String message = "Expected %s to be an Image".formatted(object); - throw new DocxStamperException(message); - } - - /** - * Resolves an image and adds it to a {@link WordprocessingMLPackage} - * document. - * - * @param document The WordprocessingMLPackage document - * @param image The image to be resolved and added - * - * @return The run containing the added image - * - * @throws OfficeStamperException If an error occurs while adding the image to the document - */ - public R resolve(WordprocessingMLPackage document, Image image) { - try { - // TODO_LATER: adding the same image twice will put the image twice into the docx-zip file. make the second - // addition of the same image a reference instead. - return RunUtil.createRunWithImage(image.getMaxWidth(), createImagePart(document, image.getImageBytes())); - } catch (Exception e) { - throw new OfficeStamperException("Error while adding image to document!", e); - } - } -} diff --git a/src/main/java/pro/verron/officestamper/preset/LocalDateResolver.java b/src/main/java/pro/verron/officestamper/preset/LocalDateResolver.java deleted file mode 100644 index ee455629..00000000 --- a/src/main/java/pro/verron/officestamper/preset/LocalDateResolver.java +++ /dev/null @@ -1,41 +0,0 @@ -package pro.verron.officestamper.preset; - -import pro.verron.officestamper.api.StringResolver; - -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; - -/** - * Resolves {@link LocalDate} objects by formatting them with a {@link DateTimeFormatter}. - * - * @author Joseph Verron - * @version ${version} - * @since 1.6.4 - */ -public final class LocalDateResolver - extends StringResolver { - private final DateTimeFormatter formatter; - - /** - * Uses {@link DateTimeFormatter#ISO_LOCAL_DATE} for formatting. - */ - public LocalDateResolver() { - this(DateTimeFormatter.ISO_LOCAL_DATE); - } - - /** - * Uses the given formatter for formatting. - * - * @param formatter the formatter to use. - */ - public LocalDateResolver(DateTimeFormatter formatter) { - super(LocalDate.class); - this.formatter = formatter; - } - - /** {@inheritDoc} */ - @Override - protected String resolve(LocalDate localDateTime) { - return localDateTime.format(formatter); - } -} diff --git a/src/main/java/pro/verron/officestamper/preset/LocalDateTimeResolver.java b/src/main/java/pro/verron/officestamper/preset/LocalDateTimeResolver.java deleted file mode 100644 index 02e746d6..00000000 --- a/src/main/java/pro/verron/officestamper/preset/LocalDateTimeResolver.java +++ /dev/null @@ -1,42 +0,0 @@ -package pro.verron.officestamper.preset; - -import pro.verron.officestamper.api.StringResolver; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; - -/** - * Resolves {@link LocalDateTime} values to a formatted string. - * - * @author Joseph Verron - * @version ${version} - * @since 1.6.4 - */ -public final class LocalDateTimeResolver - extends StringResolver { - private final DateTimeFormatter formatter; - - /** - * Creates a new resolver that uses {@link DateTimeFormatter#ISO_LOCAL_DATE_TIME} to format {@link LocalDateTime} - * values. - */ - public LocalDateTimeResolver() { - this(DateTimeFormatter.ISO_LOCAL_DATE_TIME); - } - - /** - * Creates a new resolver that uses the given formatter to format {@link LocalDateTime} values. - * - * @param formatter the formatter to use. - */ - public LocalDateTimeResolver(DateTimeFormatter formatter) { - super(LocalDateTime.class); - this.formatter = formatter; - } - - /** {@inheritDoc} */ - @Override - protected String resolve(LocalDateTime localDateTime) { - return localDateTime.format(formatter); - } -} diff --git a/src/main/java/pro/verron/officestamper/preset/LocalTimeResolver.java b/src/main/java/pro/verron/officestamper/preset/LocalTimeResolver.java deleted file mode 100644 index ff8fd592..00000000 --- a/src/main/java/pro/verron/officestamper/preset/LocalTimeResolver.java +++ /dev/null @@ -1,41 +0,0 @@ -package pro.verron.officestamper.preset; - -import pro.verron.officestamper.api.StringResolver; - -import java.time.LocalTime; -import java.time.format.DateTimeFormatter; - -/** - * Resolves {@link LocalTime} values to the format specified by the {@link DateTimeFormatter} passed to the constructor. - * - * @author Joseph Verron - * @version ${version} - * @since 1.6.4 - */ -public final class LocalTimeResolver - extends StringResolver { - private final DateTimeFormatter formatter; - - /** - * Uses {@link DateTimeFormatter#ISO_LOCAL_TIME} for formatting. - */ - public LocalTimeResolver() { - this(DateTimeFormatter.ISO_LOCAL_TIME); - } - - /** - *

Constructor for LocalTimeResolver.

- * - * @param formatter a date time pattern as specified by {@link DateTimeFormatter#ofPattern(String)} - */ - public LocalTimeResolver(DateTimeFormatter formatter) { - super(LocalTime.class); - this.formatter = formatter; - } - - /** {@inheritDoc} */ - @Override - protected String resolve(LocalTime localTime) { - return localTime.format(formatter); - } -} diff --git a/src/main/java/pro/verron/officestamper/preset/Null2DefaultResolver.java b/src/main/java/pro/verron/officestamper/preset/Null2DefaultResolver.java deleted file mode 100644 index 04065a25..00000000 --- a/src/main/java/pro/verron/officestamper/preset/Null2DefaultResolver.java +++ /dev/null @@ -1,54 +0,0 @@ -package pro.verron.officestamper.preset; - -import org.docx4j.openpackaging.packages.WordprocessingMLPackage; -import org.docx4j.wml.R; -import org.springframework.lang.Nullable; -import org.wickedsource.docxstamper.util.RunUtil; -import pro.verron.officestamper.api.ObjectResolver; - -/** - * The Null2DefaultResolver class is an implementation of the - * {@link ObjectResolver} interface - * that resolves null objects by creating a run with a default text value. - * - * @deprecated will not be removed, but will be made package-private - * - * @author Joseph Verron - * @version ${version} - * @since 1.6.7 - */ - -@Deprecated(since = "1.6.7") -public class Null2DefaultResolver - implements ObjectResolver { - - private final String text; - - /* package */ - public Null2DefaultResolver(String text) { - this.text = text; - } - - @Override - public boolean canResolve(@Nullable Object object) { - return object == null; - } - - @Override - public R resolve( - WordprocessingMLPackage document, - String expression, - Object object - ) { - return RunUtil.create(text); - } - - /** - * Retrieves the default value of the {@link Null2DefaultResolver} object. - * - * @return the default value of the {@link Null2DefaultResolver} object as a String - */ - public String defaultValue() { - return text; - } -} diff --git a/src/main/java/pro/verron/officestamper/preset/Null2PlaceholderResolver.java b/src/main/java/pro/verron/officestamper/preset/Null2PlaceholderResolver.java deleted file mode 100644 index 57c7178f..00000000 --- a/src/main/java/pro/verron/officestamper/preset/Null2PlaceholderResolver.java +++ /dev/null @@ -1,49 +0,0 @@ -package pro.verron.officestamper.preset; - -import org.docx4j.openpackaging.packages.WordprocessingMLPackage; -import org.docx4j.wml.R; -import org.springframework.lang.Nullable; -import org.wickedsource.docxstamper.api.DocxStamperException; -import org.wickedsource.docxstamper.util.RunUtil; -import pro.verron.officestamper.api.ObjectResolver; -import pro.verron.officestamper.api.Placeholder; - -/** - * The {@link Null2PlaceholderResolver} class is an implementation of the ObjectResolver interface. - * It provides a way to resolve null objects by not replacing their expression. - * - * @author Joseph Verron - * @version ${version} - * @since 1.6.7 - */ -public class Null2PlaceholderResolver - implements ObjectResolver { - - /* package */ - public Null2PlaceholderResolver() { - //DO NOTHING - } - - @Override - public boolean canResolve(@Nullable Object object) { - return object == null; - } - - @Override - public R resolve( - WordprocessingMLPackage document, - Placeholder placeholder, - Object object - ) { - return RunUtil.create(placeholder.expression()); - } - - @Override - public R resolve( - WordprocessingMLPackage document, - String expression, - Object object - ) { - throw new DocxStamperException("Should not be called"); - } -} diff --git a/src/main/java/pro/verron/officestamper/preset/Resolvers.java b/src/main/java/pro/verron/officestamper/preset/Resolvers.java index 92d2dc99..7fe53b29 100644 --- a/src/main/java/pro/verron/officestamper/preset/Resolvers.java +++ b/src/main/java/pro/verron/officestamper/preset/Resolvers.java @@ -1,13 +1,25 @@ package pro.verron.officestamper.preset; +import org.docx4j.openpackaging.packages.WordprocessingMLPackage; +import org.docx4j.wml.R; +import org.springframework.lang.Nullable; +import org.wickedsource.docxstamper.api.DocxStamperException; +import org.wickedsource.docxstamper.util.RunUtil; import pro.verron.officestamper.api.ObjectResolver; +import pro.verron.officestamper.api.OfficeStamperException; +import pro.verron.officestamper.api.Placeholder; +import pro.verron.officestamper.api.StringResolver; +import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; +import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Date; +import static org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage.createImagePart; + /** * This class provides static methods to create different types of * {@link ObjectResolver}. @@ -161,4 +173,327 @@ public static ObjectResolver legacyDate(DateTimeFormatter formatter) { public static ObjectResolver image() { return new ImageResolver(); } + + /** + * This {@link ObjectResolver} creates a formatted date {@link String} for + * expressions that return a {@link Date} object. + * + * @author Joseph Verron + * @version ${version} + * @since 1.6.7 + */ + private static final class DateResolver + extends StringResolver { + + private final DateTimeFormatter formatter; + + /** + * Creates a new DateResolver that uses the format "dd.MM.yyyy". + */ + public DateResolver() { + this(DateTimeFormatter.ofPattern("dd.MM.yyyy")); + } + + /** + * Creates a new DateResolver. + * + * @param formatter the format to use for date formatting. See + * {@link SimpleDateFormat}. + */ + public DateResolver(DateTimeFormatter formatter) { + super(Date.class); + this.formatter = formatter; + } + + /** + * Resolves a formatted date string for the given {@link Date} object. + * + * @param date the {@link Date} object to be resolved. + * + * @return the formatted date string. + */ + @Override + protected String resolve(Date date) { + var zone = ZoneId.systemDefault(); + var localDate = date.toInstant() + .atZone(zone) + .toLocalDate(); + return formatter.format(localDate); + } + } + + /** + * This {@link ObjectResolver} allows context objects to return objects of + * type {@link Image}. An expression that resolves to an {@link Image} + * object will be replaced by an actual image in the resulting .docx document. + * The image will be put as an inline into the surrounding paragraph of text. + * + * @author Joseph Verron + * @version ${version} + * @since 1.6.7 + */ + private static class ImageResolver + implements ObjectResolver { + + @Override + public boolean canResolve(@Nullable Object object) { + return object instanceof Image; + } + + @Override + public R resolve( + WordprocessingMLPackage document, + String expression, + Object object + ) { + if (object instanceof Image image) + return resolve(document, image); + String message = "Expected %s to be an Image".formatted(object); + throw new DocxStamperException(message); + } + + /** + * Resolves an image and adds it to a {@link WordprocessingMLPackage} + * document. + * + * @param document The WordprocessingMLPackage document + * @param image The image to be resolved and added + * + * @return The run containing the added image + * + * @throws OfficeStamperException If an error occurs while adding the image to the document + */ + public R resolve(WordprocessingMLPackage document, Image image) { + try { + // TODO_LATER: adding the same image twice will put the image twice into the docx-zip file. make the + // second + // addition of the same image a reference instead. + return RunUtil.createRunWithImage(image.getMaxWidth(), + createImagePart(document, image.getImageBytes())); + } catch (Exception e) { + throw new OfficeStamperException("Error while adding image to document!", e); + } + } + } + + /** + * Resolves {@link LocalDate} objects by formatting them with a {@link DateTimeFormatter}. + * + * @author Joseph Verron + * @version ${version} + * @since 1.6.4 + */ + private static final class LocalDateResolver + extends StringResolver { + private final DateTimeFormatter formatter; + + /** + * Uses {@link DateTimeFormatter#ISO_LOCAL_DATE} for formatting. + */ + public LocalDateResolver() { + this(DateTimeFormatter.ISO_LOCAL_DATE); + } + + /** + * Uses the given formatter for formatting. + * + * @param formatter the formatter to use. + */ + public LocalDateResolver(DateTimeFormatter formatter) { + super(LocalDate.class); + this.formatter = formatter; + } + + /** {@inheritDoc} */ + @Override + protected String resolve(LocalDate localDateTime) { + return localDateTime.format(formatter); + } + } + + /** + * Resolves {@link LocalDateTime} values to a formatted string. + * + * @author Joseph Verron + * @version ${version} + * @since 1.6.4 + */ + private static final class LocalDateTimeResolver + extends StringResolver { + private final DateTimeFormatter formatter; + + /** + * Creates a new resolver that uses {@link DateTimeFormatter#ISO_LOCAL_DATE_TIME} to format + * {@link LocalDateTime} + * values. + */ + public LocalDateTimeResolver() { + this(DateTimeFormatter.ISO_LOCAL_DATE_TIME); + } + + /** + * Creates a new resolver that uses the given formatter to format {@link LocalDateTime} values. + * + * @param formatter the formatter to use. + */ + public LocalDateTimeResolver(DateTimeFormatter formatter) { + super(LocalDateTime.class); + this.formatter = formatter; + } + + /** {@inheritDoc} */ + @Override + protected String resolve(LocalDateTime localDateTime) { + return localDateTime.format(formatter); + } + } + + /** + * Resolves {@link LocalTime} values to the format specified by the {@link DateTimeFormatter} passed to the + * constructor. + * + * @author Joseph Verron + * @version ${version} + * @since 1.6.4 + */ + private static final class LocalTimeResolver + extends StringResolver { + private final DateTimeFormatter formatter; + + /** + * Uses {@link DateTimeFormatter#ISO_LOCAL_TIME} for formatting. + */ + public LocalTimeResolver() { + this(DateTimeFormatter.ISO_LOCAL_TIME); + } + + /** + *

Constructor for LocalTimeResolver.

+ * + * @param formatter a date time pattern as specified by {@link DateTimeFormatter#ofPattern(String)} + */ + public LocalTimeResolver(DateTimeFormatter formatter) { + super(LocalTime.class); + this.formatter = formatter; + } + + /** {@inheritDoc} */ + @Override + protected String resolve(LocalTime localTime) { + return localTime.format(formatter); + } + } + + /** + * The Null2DefaultResolver class is an implementation of the + * {@link ObjectResolver} interface + * that resolves null objects by creating a run with a default text value. + * + * @author Joseph Verron + * @version ${version} + * @since 1.6.7 + * @deprecated will not be removed, but will be made package-private + */ + + @Deprecated(since = "1.6.7") + public static class Null2DefaultResolver + implements ObjectResolver { + + private final String text; + + /* package */ + public Null2DefaultResolver(String text) { + this.text = text; + } + + @Override + public boolean canResolve(@Nullable Object object) { + return object == null; + } + + @Override + public R resolve( + WordprocessingMLPackage document, + String expression, + Object object + ) { + return RunUtil.create(text); + } + + /** + * Retrieves the default value of the {@link Null2DefaultResolver} object. + * + * @return the default value of the {@link Null2DefaultResolver} object as a String + */ + public String defaultValue() { + return text; + } + } + + /** + * The {@link Null2PlaceholderResolver} class is an implementation of the ObjectResolver interface. + * It provides a way to resolve null objects by not replacing their expression. + * + * @author Joseph Verron + * @version ${version} + * @since 1.6.7 + */ + private static class Null2PlaceholderResolver + implements ObjectResolver { + + /* package */ + public Null2PlaceholderResolver() { + //DO NOTHING + } + + @Override + public R resolve( + WordprocessingMLPackage document, + Placeholder placeholder, + Object object + ) { + return RunUtil.create(placeholder.expression()); + } + + @Override + public boolean canResolve(@Nullable Object object) { + return object == null; + } + + @Override + public R resolve( + WordprocessingMLPackage document, + String expression, + Object object + ) { + throw new DocxStamperException("Should not be called"); + } + } + + + /** + * This class is an implementation of the {@link ObjectResolver} interface + * that resolves objects by converting them to a string representation using the + * {@link Object#toString()} method and creating a new run with the resolved content. + *

+ * * @author Joseph Verron + * * @version ${version} + * * @since 1.6.7 + */ + private static class ToStringResolver + implements ObjectResolver { + @Override + public boolean canResolve(@Nullable Object object) { + return object != null; + } + + @Override + public R resolve( + WordprocessingMLPackage document, + String expression, + Object object + ) { + return RunUtil.create(String.valueOf(object)); + } + } } diff --git a/src/main/java/pro/verron/officestamper/preset/StringResolver.java b/src/main/java/pro/verron/officestamper/preset/StringResolver.java deleted file mode 100644 index 32332345..00000000 --- a/src/main/java/pro/verron/officestamper/preset/StringResolver.java +++ /dev/null @@ -1,21 +0,0 @@ -package pro.verron.officestamper.preset; - -/** - * @deprecated since 1.6.8, This class has been deprecated in the effort - * of the library modularization. - * It is recommended to use the - * {@link pro.verron.officestamper.api.StringResolver} class instead. - * This class will be removed in the future releases of the module. - */ -@Deprecated(since = "1.6.8", forRemoval = true) -public abstract class StringResolver - extends pro.verron.officestamper.api.StringResolver { - /** - * Creates a new StringResolver with the given type. - * - * @param type the type of object to be resolved - */ - protected StringResolver(Class type) { - super(type); - } -} diff --git a/src/main/java/pro/verron/officestamper/preset/ToStringResolver.java b/src/main/java/pro/verron/officestamper/preset/ToStringResolver.java deleted file mode 100644 index 1e63ecf7..00000000 --- a/src/main/java/pro/verron/officestamper/preset/ToStringResolver.java +++ /dev/null @@ -1,33 +0,0 @@ -package pro.verron.officestamper.preset; - -import org.docx4j.openpackaging.packages.WordprocessingMLPackage; -import org.docx4j.wml.R; -import org.springframework.lang.Nullable; -import org.wickedsource.docxstamper.util.RunUtil; -import pro.verron.officestamper.api.ObjectResolver; - -/** - * This class is an implementation of the {@link ObjectResolver} interface - * that resolves objects by converting them to a string representation using the - * {@link Object#toString()} method and creating a new run with the resolved content. - *

- * * @author Joseph Verron - * * @version ${version} - * * @since 1.6.7 - */ -public class ToStringResolver - implements ObjectResolver { - @Override - public boolean canResolve(@Nullable Object object) { - return object != null; - } - - @Override - public R resolve( - WordprocessingMLPackage document, - String expression, - Object object - ) { - return RunUtil.create(String.valueOf(object)); - } -} From f7657b51392703dad8c0e974a0f09000cf144df3 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Mon, 13 May 2024 08:09:39 +0800 Subject: [PATCH 126/134] Update version to 1.6.8-SNAPSHOT in pom.xml files The version of "docx-stamper" in both the root and officestamper-examples' pom.xml files has been updated to 1.6.8-SNAPSHOT. This change is to indicate that the project is now in a development state. --- officestamper-examples/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/officestamper-examples/pom.xml b/officestamper-examples/pom.xml index 563f188e..b114c3c3 100644 --- a/officestamper-examples/pom.xml +++ b/officestamper-examples/pom.xml @@ -18,7 +18,7 @@ pro.verron docx-stamper - 1.6.8 + 1.6.8-SNAPSHOT diff --git a/pom.xml b/pom.xml index 88b266bd..d604220a 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 pro.verron docx-stamper - 1.6.8 + 1.6.8-SNAPSHOT jar docx-stamper From 04d336e1729323b98585e70b23b600683e990475 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Mon, 13 May 2024 08:32:57 +0800 Subject: [PATCH 127/134] Add qodana.sarif.json configuration file This commit adds a new sarif configuration file, qodana.sarif.json, which includes settings for tool drivers, schemas, taxa, and various rules related to different aspects such as Language Injection, Maven, JVM languages, Kotlin, etc. --- .github/workflows/analyze.yml | 1 + qodana.sarif.json | 53150 ++++++++++++++++++++++++++++++++ 2 files changed, 53151 insertions(+) create mode 100644 qodana.sarif.json diff --git a/.github/workflows/analyze.yml b/.github/workflows/analyze.yml index 4491f948..d31d9018 100644 --- a/.github/workflows/analyze.yml +++ b/.github/workflows/analyze.yml @@ -45,6 +45,7 @@ jobs: - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2023.3 env: + args: --baseline,qodana.sarif.json QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }} - uses: github/codeql-action/analyze@v3 diff --git a/qodana.sarif.json b/qodana.sarif.json new file mode 100644 index 00000000..6689c903 --- /dev/null +++ b/qodana.sarif.json @@ -0,0 +1,53150 @@ +{ + "$schema": "https://raw.githubusercontent.com/schemastore/schemastore/master/src/schemas/json/sarif-2.1.0-rtm.5.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "QDJVMC", + "fullName": "Qodana Community for JVM", + "version": "233.14714.228", + "rules": [], + "taxa": [ + { + "id": "Language injection", + "name": "Language injection" + }, + { + "id": "Maven", + "name": "Maven" + }, + { + "id": "JVM languages", + "name": "JVM languages" + }, + { + "id": "Kotlin", + "name": "Kotlin" + }, + { + "id": "Kotlin/Style issues", + "name": "Style issues", + "relationships": [ + { + "target": { + "id": "Kotlin", + "index": 3, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Kotlin/Redundant constructs", + "name": "Redundant constructs", + "relationships": [ + { + "target": { + "id": "Kotlin", + "index": 3, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java", + "name": "Java" + }, + { + "id": "Java/Portability", + "name": "Portability", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Threading issues", + "name": "Threading issues", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Internationalization", + "name": "Internationalization", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Performance", + "name": "Performance", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Class structure", + "name": "Class structure", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Code style issues", + "name": "Code style issues", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Probable bugs", + "name": "Probable bugs", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Declaration redundancy", + "name": "Declaration redundancy", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Error handling", + "name": "Error handling", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Kotlin/Migration", + "name": "Migration", + "relationships": [ + { + "target": { + "id": "Kotlin", + "index": 3, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Pattern validation", + "name": "Pattern validation" + }, + { + "id": "Java/Serialization issues", + "name": "Serialization issues", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Performance/Embedded", + "name": "Embedded", + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Groovy", + "name": "Groovy" + }, + { + "id": "Groovy/GPath", + "name": "GPath", + "relationships": [ + { + "target": { + "id": "Groovy", + "index": 20, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Imports", + "name": "Imports", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Data flow", + "name": "Data flow", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Kotlin/Probable bugs", + "name": "Probable bugs", + "relationships": [ + { + "target": { + "id": "Kotlin", + "index": 3, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Inheritance issues", + "name": "Inheritance issues", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Control flow issues", + "name": "Control flow issues", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Numeric issues", + "name": "Numeric issues", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Initialization", + "name": "Initialization", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "HTML", + "name": "HTML" + }, + { + "id": "Java/Security", + "name": "Security", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfig", + "name": "EditorConfig" + }, + { + "id": "Properties files", + "name": "Properties files" + }, + { + "id": "Java/JavaBeans issues", + "name": "JavaBeans issues", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Assignment issues", + "name": "Assignment issues", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Groovy/Naming conventions", + "name": "Naming conventions", + "relationships": [ + { + "target": { + "id": "Groovy", + "index": 20, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Packaging issues", + "name": "Packaging issues", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Verbose or redundant code constructs", + "name": "Verbose or redundant code constructs", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Groovy/Threading issues", + "name": "Threading issues", + "relationships": [ + { + "target": { + "id": "Groovy", + "index": 20, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Gradle", + "name": "Gradle" + }, + { + "id": "Gradle/Probable bugs", + "name": "Probable bugs", + "relationships": [ + { + "target": { + "id": "Gradle", + "index": 39, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Plugin DevKit", + "name": "Plugin DevKit" + }, + { + "id": "Plugin DevKit/Code", + "name": "Code", + "relationships": [ + { + "target": { + "id": "Plugin DevKit", + "index": 41, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Groovy/Error handling", + "name": "Error handling", + "relationships": [ + { + "target": { + "id": "Groovy", + "index": 20, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "General", + "name": "General" + }, + { + "id": "Java/Javadoc", + "name": "Javadoc", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JVM languages/Logging", + "name": "Logging", + "relationships": [ + { + "target": { + "id": "JVM languages", + "index": 2, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Resource management", + "name": "Resource management", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Gradle/Validity issues", + "name": "Validity issues", + "relationships": [ + { + "target": { + "id": "Gradle", + "index": 39, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Kotlin/Naming conventions", + "name": "Naming conventions", + "relationships": [ + { + "target": { + "id": "Kotlin", + "index": 3, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Code maturity", + "name": "Code maturity", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Naming conventions", + "name": "Naming conventions", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Java language level migration aids", + "name": "Java language level migration aids", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Java language level migration aids/Java 5", + "name": "Java 5", + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids", + "index": 52, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Kotlin/Other problems", + "name": "Other problems", + "relationships": [ + { + "target": { + "id": "Kotlin", + "index": 3, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Groovy/Probable bugs", + "name": "Probable bugs", + "relationships": [ + { + "target": { + "id": "Groovy", + "index": 20, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Plugin DevKit/Workspace model", + "name": "Workspace model", + "relationships": [ + { + "target": { + "id": "Plugin DevKit", + "index": 41, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Visibility", + "name": "Visibility", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Lombok", + "name": "Lombok", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Lombok/Redundant definitions", + "name": "Redundant definitions", + "relationships": [ + { + "target": { + "id": "Java/Lombok", + "index": 58, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Encapsulation", + "name": "Encapsulation", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Concurrency annotation issues", + "name": "Concurrency annotation issues", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Java language level migration aids/Java 8", + "name": "Java 8", + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids", + "index": 52, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Java language level migration aids/Java 20", + "name": "Java 20", + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids", + "index": 52, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Java language level issues", + "name": "Java language level issues", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "XML", + "name": "XML" + }, + { + "id": "Java/Finalization", + "name": "Finalization", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Groovy/Assignment issues", + "name": "Assignment issues", + "relationships": [ + { + "target": { + "id": "Groovy", + "index": 20, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Logging", + "name": "Logging", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Modularization issues", + "name": "Modularization issues", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Kotlin/Java interop issues", + "name": "Java interop issues", + "relationships": [ + { + "target": { + "id": "Kotlin", + "index": 3, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Naming conventions/Class", + "name": "Class", + "relationships": [ + { + "target": { + "id": "Java/Naming conventions", + "index": 51, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Groovy/Control flow issues", + "name": "Control flow issues", + "relationships": [ + { + "target": { + "id": "Groovy", + "index": 20, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Memory", + "name": "Memory", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Groovy/Potentially confusing code constructs", + "name": "Potentially confusing code constructs", + "relationships": [ + { + "target": { + "id": "Groovy", + "index": 20, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/TestNG", + "name": "TestNG", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Plugin DevKit/Plugin descriptor", + "name": "Plugin descriptor", + "relationships": [ + { + "target": { + "id": "Plugin DevKit", + "index": 41, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/JUnit", + "name": "JUnit", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Abstraction issues", + "name": "Abstraction issues", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Java language level migration aids/Java 19", + "name": "Java 19", + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids", + "index": 52, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Java language level migration aids/Java 9", + "name": "Java 9", + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids", + "index": 52, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Groovy/Other", + "name": "Other", + "relationships": [ + { + "target": { + "id": "Groovy", + "index": 20, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Cloning issues", + "name": "Cloning issues", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RegExp", + "name": "RegExp" + }, + { + "id": "Groovy/Style", + "name": "Style", + "relationships": [ + { + "target": { + "id": "Groovy", + "index": 20, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UI form", + "name": "UI form" + }, + { + "id": "Java/Lombok/Redundant modifiers", + "name": "Redundant modifiers", + "relationships": [ + { + "target": { + "id": "Java/Lombok", + "index": 58, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Class metrics", + "name": "Class metrics", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Naming conventions/Method", + "name": "Method", + "relationships": [ + { + "target": { + "id": "Java/Naming conventions", + "index": 51, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Dependency issues", + "name": "Dependency issues", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Compiler issues", + "name": "Compiler issues", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Groovy/Annotations", + "name": "Annotations", + "relationships": [ + { + "target": { + "id": "Groovy", + "index": 20, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Proofreading", + "name": "Proofreading" + }, + { + "id": "Manifest", + "name": "Manifest" + }, + { + "id": "JVM languages/Test frameworks", + "name": "Test frameworks", + "relationships": [ + { + "target": { + "id": "JVM languages", + "index": 2, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Method metrics", + "name": "Method metrics", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Test frameworks", + "name": "Test frameworks", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Bitwise operation issues", + "name": "Bitwise operation issues", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Reflective access", + "name": "Reflective access", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Java language level migration aids/Java 15", + "name": "Java 15", + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids", + "index": 52, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Java language level migration aids/Java 14", + "name": "Java 14", + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids", + "index": 52, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Groovy/Validity issues", + "name": "Validity issues", + "relationships": [ + { + "target": { + "id": "Groovy", + "index": 20, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Numeric issues/Cast", + "name": "Cast", + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Java language level migration aids/Java 21", + "name": "Java 21", + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids", + "index": 52, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Plugin DevKit/Description file", + "name": "Description file", + "relationships": [ + { + "target": { + "id": "Plugin DevKit", + "index": 41, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JSON and JSON5", + "name": "JSON and JSON5" + }, + { + "id": "Groovy/Data flow", + "name": "Data flow", + "relationships": [ + { + "target": { + "id": "Groovy", + "index": 20, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Groovy/Method metrics", + "name": "Method metrics", + "relationships": [ + { + "target": { + "id": "Groovy", + "index": 20, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Kotlin/Migration/Maven", + "name": "Maven", + "relationships": [ + { + "target": { + "id": "Kotlin/Migration", + "index": 16, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Internationalization", + "name": "Internationalization" + }, + { + "id": "Java/Java language level migration aids/Java 10", + "name": "Java 10", + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids", + "index": 52, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Java language level migration aids/Java 16", + "name": "Java 16", + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids", + "index": 52, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Java language level migration aids/Java 7", + "name": "Java 7", + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids", + "index": 52, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Version control", + "name": "Version control" + }, + { + "id": "Kotlin/Migration/Gradle", + "name": "Gradle", + "relationships": [ + { + "target": { + "id": "Kotlin/Migration", + "index": 16, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Probable bugs/Nullability problems", + "name": "Nullability problems", + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Kotlin/Logging", + "name": "Logging", + "relationships": [ + { + "target": { + "id": "Kotlin", + "index": 3, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Structural search", + "name": "Structural search" + }, + { + "id": "Java/toString() issues", + "name": "toString() issues", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RELAX NG", + "name": "RELAX NG" + }, + { + "id": "Java/Properties files", + "name": "Properties files", + "relationships": [ + { + "target": { + "id": "Java", + "index": 6, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java/Java language level migration aids/Java 11", + "name": "Java 11", + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids", + "index": 52, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Qodana", + "name": "Qodana" + }, + { + "id": "Kotlin/React", + "name": "React", + "relationships": [ + { + "target": { + "id": "Kotlin", + "index": 3, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Kotlin/React/Probable bugs", + "name": "Probable bugs", + "relationships": [ + { + "target": { + "id": "Kotlin/React", + "index": 123, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Gradle/Best practises", + "name": "Best practises", + "relationships": [ + { + "target": { + "id": "Gradle", + "index": 39, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Groovy/Declaration redundancy", + "name": "Declaration redundancy", + "relationships": [ + { + "target": { + "id": "Groovy", + "index": 20, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + } + ], + "language": "en-US", + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "isComprehensive": false + }, + "extensions": [ + { + "name": "org.intellij.intelliLang", + "version": "233.14714", + "rules": [ + { + "id": "InjectionNotApplicable", + "shortDescription": { + "text": "Injection Annotation not applicable" + }, + "fullDescription": { + "text": "Reports when a '@Language' annotation is applied to an element with a type other than 'String' or 'String[]'. Example: '@Language(\"HTML\") int i;' After the quick-fix is applied: 'int i;'", + "markdown": "Reports when a `@Language` annotation is applied to an element with a type other than `String` or `String[]`.\n\n**Example:**\n\n\n @Language(\"HTML\") int i;\n\nAfter the quick-fix is applied:\n\n\n int i;\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "InjectionNotApplicable", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Language injection", + "index": 0, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PatternNotApplicable", + "shortDescription": { + "text": "Pattern Annotation not applicable" + }, + "fullDescription": { + "text": "Reports when a '@Pattern' annotation is applied to an element with a type other than 'String'. Example: '@Pattern(\"\\\\d\\\\d\") int i;'", + "markdown": "Reports when a `@Pattern` annotation is applied to an element with a type other than `String`.\n\n**Example:**\n\n\n @Pattern(\"\\\\d\\\\d\") int i;\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "PatternNotApplicable", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Pattern validation", + "index": 17, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnknownLanguage", + "shortDescription": { + "text": "Unknown Language ID" + }, + "fullDescription": { + "text": "Reports when the ID of the language used in a '@Language' annotation is unknown. Example: '@Language(\"HMTL\") String html;'", + "markdown": "Reports when the ID of the language used in a `@Language` annotation is unknown.\n\n**Example:**\n\n\n @Language(\"HMTL\") String html;\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "UnknownLanguage", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Language injection", + "index": 0, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PatternOverriddenByNonAnnotatedMethod", + "shortDescription": { + "text": "Non-annotated Method overrides @Pattern Method" + }, + "fullDescription": { + "text": "Reports when a method without any '@Pattern' annotation overrides a '@Pattern' annotated method. This does not prevent error-highlighting inside the editor, however the overriding method will not be checked at runtime. A quick fix is provided to add a '@Pattern' annotation that matches the one from the superclass method. This ensures the runtime-check instrumentation works correctly. Example: 'abstract class Parent {\n abstract @Pattern(\"\\\\d\\\\d-\\\\d\\\\d\\\\d\") String getId();\n }\n class Child extends Parent {\n @Override String getId() { // warning here\n return \"12-345\";\n }\n }'", + "markdown": "Reports when a method without any `@Pattern` annotation overrides a `@Pattern` annotated method. This does not prevent error-highlighting inside the editor, however the overriding method will not be checked at runtime.\n\n\nA quick fix is provided to add a `@Pattern` annotation that matches the one from the superclass method. This ensures the\nruntime-check instrumentation works correctly.\n\n**Example:**\n\n\n abstract class Parent {\n abstract @Pattern(\"\\\\d\\\\d-\\\\d\\\\d\\\\d\") String getId();\n }\n class Child extends Parent {\n @Override String getId() { // warning here\n return \"12-345\";\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PatternOverriddenByNonAnnotatedMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Pattern validation", + "index": 17, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InjectedReferences", + "shortDescription": { + "text": "Injected references" + }, + "fullDescription": { + "text": "Reports unresolved references injected by Language Injections. Example: '@Language(\"file-reference\")\n String fileName = \"/home/user/nonexistent.file\"; // highlighted if file doesn't exist'", + "markdown": "Reports unresolved references injected by [Language Injections](https://www.jetbrains.com/help/idea/using-language-injections.html).\n\nExample:\n\n\n @Language(\"file-reference\")\n String fileName = \"/home/user/nonexistent.file\"; // highlighted if file doesn't exist\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "InjectedReferences", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "General", + "index": 44, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PatternValidation", + "shortDescription": { + "text": "Validate annotated patterns" + }, + "fullDescription": { + "text": "Reports expressions passed as arguments for '@Pattern' parameters and returned from '@Pattern'-annotated methods that do not match the specified pattern. Example: '@Pattern(\"\\\\d\\\\d-\\\\d\\\\d\\\\d\") String getId() {\n return \"1\";\n }' Use the Flag non compile-time constant expressions option to let the inspection report expressions with an unknown value and offer to add a substitution ('@Subst') annotation.", + "markdown": "Reports expressions passed as arguments for `@Pattern` parameters and returned from `@Pattern`-annotated methods that do not match the specified pattern.\n\n**Example:**\n\n\n @Pattern(\"\\\\d\\\\d-\\\\d\\\\d\\\\d\") String getId() {\n return \"1\";\n }\n\n\nUse the **Flag non compile-time constant expressions** option to let the inspection report expressions with\nan unknown value and offer to add a substitution (`@Subst`) annotation." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PatternValidation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Pattern validation", + "index": 17, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LanguageMismatch", + "shortDescription": { + "text": "Language mismatch" + }, + "fullDescription": { + "text": "Reports when the language of a reference does not match the expected language of the usage context. Example: '@Language(\"JavaScript\")\n String JS_CODE = \"var x;\";\n\n @Language(\"XPath\")\n String XPATH_CODE = JS_CODE; // warning here'", + "markdown": "Reports when the language of a reference does not match the expected language of the usage context.\n\nExample:\n\n\n @Language(\"JavaScript\")\n String JS_CODE = \"var x;\";\n\n @Language(\"XPath\")\n String XPATH_CODE = JS_CODE; // warning here\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LanguageMismatch", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Language injection", + "index": 0, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + } + ], + "language": "en-US", + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "isComprehensive": false + }, + { + "name": "org.jetbrains.idea.maven", + "version": "233.14714", + "rules": [ + { + "id": "MavenRedundantGroupId", + "shortDescription": { + "text": "Redundant groupId" + }, + "fullDescription": { + "text": "Reports the unnecessary definition since it is already defined in the parent pom.xml", + "markdown": "Reports the unnecessary \\ definition since it is already defined in the parent pom.xml" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MavenRedundantGroupId", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Maven", + "index": 1, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MavenDuplicatePluginInspection", + "shortDescription": { + "text": "Duplicate plugin declaration" + }, + "fullDescription": { + "text": "Reports the duplication of the plugin declaration in pom.xml", + "markdown": "Reports the duplication of the plugin declaration in pom.xml" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MavenDuplicatePluginInspection", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Maven", + "index": 1, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MavenPropertyInParent", + "shortDescription": { + "text": "Usage of properties in parent description" + }, + "fullDescription": { + "text": "Reports that the usage of properties in modules parent definition is prohibited", + "markdown": "Reports that the usage of properties in modules parent definition is prohibited" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MavenPropertyInParent", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Maven", + "index": 1, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MavenDuplicateDependenciesInspection", + "shortDescription": { + "text": "Duplicate Dependencies" + }, + "fullDescription": { + "text": "Reports duplicate dependencies", + "markdown": "Reports duplicate dependencies" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MavenDuplicateDependenciesInspection", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Maven", + "index": 1, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MavenModelInspection", + "shortDescription": { + "text": "Maven Model Inspection" + }, + "fullDescription": { + "text": "Reports resolution problems in a Maven model", + "markdown": "Reports resolution problems in a Maven model" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "MavenModelInspection", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Maven", + "index": 1, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MavenParentMissedVersionInspection", + "shortDescription": { + "text": "Parent version missed" + }, + "fullDescription": { + "text": "Reports the absence of the parent version element for versions that do not support consumer POM feature", + "markdown": "Reports the absence of the parent version element for versions that do not support consumer POM feature" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "MavenParentMissedVersionInspection", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Maven", + "index": 1, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + } + ], + "language": "en-US", + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "isComprehensive": false + }, + { + "name": "com.intellij.java", + "version": "233.14714", + "rules": [ + { + "id": "OverrideOnly", + "shortDescription": { + "text": "Method can only be overridden" + }, + "fullDescription": { + "text": "Reports calls to API methods marked with '@ApiStatus.OverrideOnly'. The '@ApiStatus.OverrideOnly' annotation indicates that the method is part of SPI (Service Provider Interface). Clients of the declaring library should implement or override such methods, not call them directly. Marking a class or interface with this annotation is the same as marking every method with it.", + "markdown": "Reports calls to API methods marked with `@ApiStatus.OverrideOnly`.\n\n\nThe `@ApiStatus.OverrideOnly` annotation indicates that the method is part of SPI (Service Provider Interface).\nClients of the declaring library should implement or override such methods, not call them directly.\nMarking a class or interface with this annotation is the same as marking every method with it." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "OverrideOnly", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages", + "index": 2, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RuntimeExec", + "shortDescription": { + "text": "Call to 'Runtime.exec()'" + }, + "fullDescription": { + "text": "Reports calls to 'Runtime.exec()' or any of its variants. Calls to 'Runtime.exec()' are inherently unportable.", + "markdown": "Reports calls to `Runtime.exec()` or any of its variants. Calls to `Runtime.exec()` are inherently unportable." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToRuntimeExec", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Portability", + "index": 7, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StaticInitializerReferencesSubClass", + "shortDescription": { + "text": "Static initializer references subclass" + }, + "fullDescription": { + "text": "Reports classes that refer to their subclasses in static initializers or static fields. Such references can cause JVM-level deadlocks in multithreaded environment, when one thread tries to load the superclass and another thread tries to load the subclass at the same time. Example: 'class Parent {\n static final Child field = new Child();\n }\n class Child extends Parent { }'", + "markdown": "Reports classes that refer to their subclasses in static initializers or static fields.\n\nSuch references can cause JVM-level deadlocks in multithreaded environment, when one thread tries to load the superclass\nand another thread tries to load the subclass at the same time.\n\n**Example:**\n\n\n class Parent {\n static final Child field = new Child();\n }\n class Child extends Parent { }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "StaticInitializerReferencesSubClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MissingDeprecatedAnnotationOnScheduledForRemovalApi", + "shortDescription": { + "text": "Missing '@Deprecated' annotation on scheduled for removal API" + }, + "fullDescription": { + "text": "Reports declarations marked with '@ApiStatus.ScheduledForRemoval' without '@Deprecated'. Example: '@ApiStatus.ScheduledForRemoval(inVersion = \"2017.3\")\n public void myLegacyMethod() { }' After the quick-fix is applied the result looks like: '@Deprecated\n @ApiStatus.ScheduledForRemoval(inVersion = \"2017.3\")\n public void myLegacyMethod() { }'", + "markdown": "Reports declarations marked with `@ApiStatus.ScheduledForRemoval` without `@Deprecated`.\n\nExample:\n\n\n @ApiStatus.ScheduledForRemoval(inVersion = \"2017.3\")\n public void myLegacyMethod() { }\n\nAfter the quick-fix is applied the result looks like:\n\n\n @Deprecated\n @ApiStatus.ScheduledForRemoval(inVersion = \"2017.3\")\n public void myLegacyMethod() { }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "error", + "parameters": { + "suppressToolId": "MissingDeprecatedAnnotationOnScheduledForRemovalApi", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages", + "index": 2, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CallToSuspiciousStringMethod", + "shortDescription": { + "text": "Call to suspicious 'String' method" + }, + "fullDescription": { + "text": "Reports calls of: 'equals()' 'equalsIgnoreCase()' 'compareTo()' 'compareToIgnoreCase()' and 'trim()' on 'String' objects. Comparison of internationalized strings should probably use a 'java.text.Collator' instead. 'String.trim()' only removes control characters between 0x00 and 0x20. The 'String.strip()' method introduced in Java 11 is more Unicode aware and can be used as a replacement.", + "markdown": "Reports calls of:\n\n* `equals()`\n* `equalsIgnoreCase()`\n* `compareTo()`\n* `compareToIgnoreCase()` and\n* `trim()`\n\n\non `String` objects.\nComparison of internationalized strings should probably use a `java.text.Collator` instead.\n`String.trim()` only removes control characters between 0x00 and 0x20.\nThe `String.strip()` method introduced in Java 11 is more Unicode aware and can be used as a replacement." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToSuspiciousStringMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Internationalization", + "index": 9, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KeySetIterationMayUseEntrySet", + "shortDescription": { + "text": "Iteration over 'keySet()' can be optimized" + }, + "fullDescription": { + "text": "Reports iterations over the 'keySet()' of a 'java.util.Map' instance, where the iterated keys are used to retrieve the values from the map. Such iteration may be more efficient when replaced with an iteration over the 'entrySet()' or 'values()' (if the key is not actually used). Similarly, 'keySet().forEach(key -> ...)' can be replaced with 'forEach((key, value) -> ...)' if values are retrieved inside a lambda. Example: 'for (Object key : map.keySet()) {\n Object val = map.get(key);\n }' After the quick-fix is applied: 'for (Object val : map.values()) {}'", + "markdown": "Reports iterations over the `keySet()` of a `java.util.Map` instance, where the iterated keys are used to retrieve the values from the map.\n\n\nSuch iteration may be more efficient when replaced with an iteration over the\n`entrySet()` or `values()` (if the key is not actually used).\n\n\nSimilarly, `keySet().forEach(key -> ...)`\ncan be replaced with `forEach((key, value) -> ...)` if values are retrieved\ninside a lambda.\n\n**Example:**\n\n\n for (Object key : map.keySet()) {\n Object val = map.get(key);\n }\n\nAfter the quick-fix is applied:\n\n\n for (Object val : map.values()) {}\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "KeySetIterationMayUseEntrySet", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConstantDeclaredInAbstractClass", + "shortDescription": { + "text": "Constant declared in 'abstract' class" + }, + "fullDescription": { + "text": "Reports constants ('public static final' fields) declared in abstract classes. Some coding standards require declaring constants in interfaces instead.", + "markdown": "Reports constants (`public static final` fields) declared in abstract classes.\n\nSome coding standards require declaring constants in interfaces instead." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ConstantDeclaredInAbstractClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryQualifierForThis", + "shortDescription": { + "text": "Unnecessary qualifier for 'this' or 'super'" + }, + "fullDescription": { + "text": "Reports unnecessary qualification of 'this' or 'super'. Using a qualifier on 'this' or 'super' to disambiguate a code reference may easily become unnecessary via automatic refactorings and should be deleted for clarity. Example: 'class Foo {\n void foo() {}\n }\n\n class Bar extends Foo {\n void bar() {\n Bar.super.foo();\n }\n }' After the quick-fix is applied: 'class Foo {\n void foo() {}\n }\n\n class Bar extends Foo {\n void bar() {\n super.foo();\n }\n }'", + "markdown": "Reports unnecessary qualification of `this` or `super`.\n\n\nUsing a qualifier on `this` or `super` to\ndisambiguate a code reference may easily become unnecessary via automatic refactorings and should be deleted for clarity.\n\n**Example:**\n\n\n class Foo {\n void foo() {}\n }\n\n class Bar extends Foo {\n void bar() {\n Bar.super.foo();\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo {\n void foo() {}\n }\n\n class Bar extends Foo {\n void bar() {\n super.foo();\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryQualifierForThis", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SubtractionInCompareTo", + "shortDescription": { + "text": "Subtraction in 'compareTo()'" + }, + "fullDescription": { + "text": "Reports subtraction in 'compareTo()' methods and methods implementing 'java.util.Comparator.compare()'. While it is a common idiom to use the results of integer subtraction as the result of a 'compareTo()' method, this construct may cause subtle and difficult bugs in cases of integer overflow. Comparing the integer values directly and returning '-1', '0', or '1' is a better practice in most cases. Subtraction on floating point values that is immediately cast to integral type is also reported because precision loss is possible due to rounding. The inspection doesn't report when it's statically determined that value ranges are limited, and overflow never occurs. Additionally, subtraction on 'int' numbers greater than or equal to '0' will never overflow. Therefore, this inspection tries not to warn in those cases. Methods that always return zero or greater can be marked with the 'javax.annotation.Nonnegative' annotation or specified in this inspection's options. Example: 'class DoubleHolder implements Comparable {\n double d;\n public int compareTo(DoubleHolder that) {\n return (int)(this.d - that.d);\n }\n }' A no-warning example because 'String.length()' is known to be non-negative: 'class A implements Comparable {\n final String s = \"\";\n public int compareTo(A a) {\n return s.length() - a.s.length();\n }\n }' Use the options to list methods that are safe to use inside a subtraction. Methods are safe when they return an 'int' value that is always greater than or equal to '0'.", + "markdown": "Reports subtraction in `compareTo()` methods and methods implementing `java.util.Comparator.compare()`.\n\n\nWhile it is a common idiom to\nuse the results of integer subtraction as the result of a `compareTo()`\nmethod, this construct may cause subtle and difficult bugs in cases of integer overflow.\nComparing the integer values directly and returning `-1`, `0`, or `1` is a better practice in most cases.\n\n\nSubtraction on floating point values that is immediately cast to integral type is also reported because precision loss is possible due to\nrounding.\n\n\nThe inspection doesn't report when it's statically determined that value ranges are limited, and overflow never occurs.\nAdditionally, subtraction on `int` numbers greater than or equal to `0` will never overflow.\nTherefore, this inspection tries not to warn in those cases.\n\n\nMethods that always return zero or greater can be marked with the\n`javax.annotation.Nonnegative` annotation or specified in this inspection's options.\n\n**Example:**\n\n\n class DoubleHolder implements Comparable {\n double d;\n public int compareTo(DoubleHolder that) {\n return (int)(this.d - that.d);\n }\n }\n\nA no-warning example because `String.length()` is known to be non-negative:\n\n\n class A implements Comparable {\n final String s = \"\";\n public int compareTo(A a) {\n return s.length() - a.s.length();\n }\n }\n\n\nUse the options to list methods that are safe to use inside a subtraction.\nMethods are safe when they return an `int` value that is always greater than or equal to `0`." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SubtractionInCompareTo", + "cweIds": [ + 682 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnusedReturnValue", + "shortDescription": { + "text": "Method can be made 'void'" + }, + "fullDescription": { + "text": "Reports methods whose return values are never used when called. The return type of such methods can be made 'void'. Methods annotated with Error Prone's or AssertJ's '@CanIgnoreReturnValue' annotation will not be reported. The quick-fix updates the method signature and removes 'return' statements from inside the method. Example: '// reported if visibility setting is Protected or Public\n protected String myToUpperCase(String s) {\n return s.toUpperCase();\n }\n\n // simple setter, reporting depends on setting\n public String setStr(String str) {\n myStr = str;\n return myStr;\n }\n\n void test() {\n setStr(\"value\"); // return value is unused\n myToUpperCase(\"result\"); // return value is unused\n }' After the quick-fix is applied to both methods: 'protected void myToUpperCase(String s) {\n // 'return' removed completely\n // as 's.toUpperCase()' has no side effect\n }\n\n public void setStr(String str) {\n myStr = str;\n // 'return' removed\n }\n ...' NOTE: Some methods might not be reported during in-editor highlighting due to performance reasons. To see all results, run the inspection using Code | Inspect Code or Code | Analyze Code | Run Inspection by Name> Use the Ignore chainable methods option to ignore unused return values from chainable calls. Use the Maximal reported method visibility option to control the maximum visibility of methods to be reported.", + "markdown": "Reports methods whose return values are never used when called. The return type of such methods can be made `void`.\n\nMethods annotated with Error Prone's or AssertJ's `@CanIgnoreReturnValue` annotation will not be reported.\nThe quick-fix updates the method signature and removes `return` statements from inside the method.\n\n**Example:**\n\n\n // reported if visibility setting is Protected or Public\n protected String myToUpperCase(String s) {\n return s.toUpperCase();\n }\n\n // simple setter, reporting depends on setting\n public String setStr(String str) {\n myStr = str;\n return myStr;\n }\n\n void test() {\n setStr(\"value\"); // return value is unused\n myToUpperCase(\"result\"); // return value is unused\n }\n\nAfter the quick-fix is applied to both methods:\n\n\n protected void myToUpperCase(String s) {\n // 'return' removed completely\n // as 's.toUpperCase()' has no side effect\n }\n\n public void setStr(String str) {\n myStr = str;\n // 'return' removed\n }\n ...\n\n\n**NOTE:** Some methods might not be reported during in-editor highlighting due to performance reasons.\nTo see all results, run the inspection using **Code \\| Inspect Code** or **Code \\| Analyze Code \\| Run Inspection by Name**\\>\n\nUse the **Ignore chainable methods** option to ignore unused return values from chainable calls.\n\nUse the **Maximal reported method visibility** option to control the maximum visibility of methods to be reported." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnusedReturnValue", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UncheckedExceptionClass", + "shortDescription": { + "text": "Unchecked 'Exception' class" + }, + "fullDescription": { + "text": "Reports subclasses of 'java.lang.RuntimeException'. Some coding standards require that all user-defined exception classes are checked. Example: 'class EnigmaException extends RuntimeException {} // warning: Unchecked exception class 'EnigmaException''", + "markdown": "Reports subclasses of `java.lang.RuntimeException`.\n\nSome coding standards require that all user-defined exception classes are checked.\n\n**Example:**\n\n\n class EnigmaException extends RuntimeException {} // warning: Unchecked exception class 'EnigmaException'\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UncheckedExceptionClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SizeReplaceableByIsEmpty", + "shortDescription": { + "text": "'size() == 0' can be replaced with 'isEmpty()'" + }, + "fullDescription": { + "text": "Reports '.size()' or '.length()' comparisons with a '0' literal that can be replaced with a call to '.isEmpty()'. Example: 'boolean emptyList = list.size() == 0;' After the quick-fix is applied: 'boolean emptyList = list.isEmpty();' Use the Ignored classes table to add classes for which any '.size()' or '.length()' comparisons should not be replaced. Use the Ignore expressions which would be replaced with '!isEmpty()' option to ignore any expressions which would be replaced with '!isEmpty()'.", + "markdown": "Reports `.size()` or `.length()` comparisons with a `0` literal that can be replaced with a call to `.isEmpty()`.\n\n**Example:**\n\n\n boolean emptyList = list.size() == 0;\n\nAfter the quick-fix is applied:\n\n\n boolean emptyList = list.isEmpty();\n \n\nUse the **Ignored classes** table to add classes for which any `.size()` or `.length()` comparisons should not be replaced.\n\nUse the **Ignore expressions which would be replaced with `!isEmpty()`** option to ignore any expressions which would be replaced with `!isEmpty()`." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SizeReplaceableByIsEmpty", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnsupportedChronoFieldUnitCall", + "shortDescription": { + "text": "Call methods with unsupported 'java.time.temporal.ChronoUnit' and 'java.time.temporal.ChronoField'" + }, + "fullDescription": { + "text": "Reports 'java.time' method calls ('get()', 'getLong()', 'with()', 'plus()', 'minus()') with unsupported 'java.time.temporal.ChronoField' or 'java.time.temporal.ChronoUnit' enum constants as arguments. Such calls will throw a 'UnsupportedTemporalTypeException' at runtime. Example: 'LocalTime localTime = LocalTime.now();\nint year = localTime.get(ChronoField.YEAR);' New in 2023.2", + "markdown": "Reports `java.time` method calls (`get()`, `getLong()`, `with()`, `plus()`, `minus()`) with unsupported `java.time.temporal.ChronoField` or `java.time.temporal.ChronoUnit` enum constants as arguments. Such calls will throw a `UnsupportedTemporalTypeException` at runtime.\n\nExample:\n\n\n LocalTime localTime = LocalTime.now();\n int year = localTime.get(ChronoField.YEAR);\n\nNew in 2023.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "UnsupportedChronoFieldUnitCall", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NumberEquality", + "shortDescription": { + "text": "Number comparison using '==', instead of 'equals()'" + }, + "fullDescription": { + "text": "Reports code that uses == or != instead of 'equals()' to test for 'Number' equality. With auto-boxing, it is easy to make the mistake of comparing two instances of a wrapper type instead of two primitives, for example 'Integer' instead of 'int'. Example: 'void foo(Integer a, Integer b) {\n final boolean bool = a == b;\n }' If 'a' is known to be non-null, then it's safe to apply the \"unsafe\" quick-fix and get the result similar to the following: 'void foo(Integer a, Integer b) {\n final boolean bool = a.equals(b);\n }'", + "markdown": "Reports code that uses **==** or **!=** instead of `equals()` to test for `Number` equality.\n\n\nWith auto-boxing, it is easy\nto make the mistake of comparing two instances of a wrapper type instead of two primitives, for example `Integer` instead of\n`int`.\n\n**Example:**\n\n void foo(Integer a, Integer b) {\n final boolean bool = a == b;\n }\n\nIf `a` is known to be non-null, then it's safe to apply the \"unsafe\" quick-fix and get the result similar to the following:\n\n void foo(Integer a, Integer b) {\n final boolean bool = a.equals(b);\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "NumberEquality", + "cweIds": [ + 480 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ComparatorNotSerializable", + "shortDescription": { + "text": "'Comparator' class not declared 'Serializable'" + }, + "fullDescription": { + "text": "Reports classes that implement 'java.lang.Comparator', but do not implement 'java.io.Serializable'. If a non-serializable comparator is used to construct an ordered collection such as a 'java.util.TreeMap' or 'java.util.TreeSet', then the collection will also be non-serializable. This can result in unexpected and difficult-to-diagnose bugs. Since subclasses of 'java.lang.Comparator' are often stateless, simply marking them serializable is a small cost to avoid such issues. Example: 'class Foo implements Comparator { // warning\n @Override\n public int compare(Object o1, Object o2) {\n /* ... */\n }\n }' After the quick-fix is applied: 'class Foo implements Comparator, Serializable { // no warning here\n @Override\n public int compare(Object o1, Object o2) {\n /* ... */\n }\n }'", + "markdown": "Reports classes that implement `java.lang.Comparator`, but do not implement `java.io.Serializable`.\n\n\nIf a non-serializable comparator is used to construct an ordered collection such\nas a `java.util.TreeMap` or `java.util.TreeSet`, then the\ncollection will also be non-serializable. This can result in unexpected and\ndifficult-to-diagnose bugs.\n\n\nSince subclasses of `java.lang.Comparator` are often stateless,\nsimply marking them serializable is a small cost to avoid such issues.\n\n**Example:**\n\n\n class Foo implements Comparator { // warning\n @Override\n public int compare(Object o1, Object o2) {\n /* ... */\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo implements Comparator, Serializable { // no warning here\n @Override\n public int compare(Object o1, Object o2) {\n /* ... */\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ComparatorNotSerializable", + "cweIds": [ + 502 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassWithOnlyPrivateConstructors", + "shortDescription": { + "text": "Class with only 'private' constructors should be declared 'final'" + }, + "fullDescription": { + "text": "Reports classes with only 'private' constructors. A class that only has 'private' constructors cannot be extended outside a file and should be declared as 'final'.", + "markdown": "Reports classes with only `private` constructors.\n\nA class that only has `private` constructors cannot be extended outside a file and should be declared as `final`." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassWithOnlyPrivateConstructors", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConnectionResource", + "shortDescription": { + "text": "Connection opened but not safely closed" + }, + "fullDescription": { + "text": "Reports Java ME 'javax.microedition.io.Connection' resources that are not opened in front of a 'try' block and closed in the corresponding 'finally' block. Such resources may be inadvertently leaked if an exception is thrown before the resource is closed. Example: 'void example() throws IOException {\n Connection c = Connector.open(\"foo\");\n }'", + "markdown": "Reports Java ME `javax.microedition.io.Connection` resources that are not opened in front of a `try` block and closed in the corresponding `finally` block. Such resources may be inadvertently leaked if an exception is thrown before the resource is closed.\n\n**Example:**\n\n\n void example() throws IOException {\n Connection c = Connector.open(\"foo\");\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ConnectionOpenedButNotSafelyClosed", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance/Embedded", + "index": 19, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UNUSED_IMPORT", + "shortDescription": { + "text": "Unused import" + }, + "fullDescription": { + "text": "Reports redundant 'import' statements. Regular 'import' statements are unnecessary when not using imported classes and packages in the source file. The same applies to imported 'static' fields and methods that aren't used in the source file. Example: 'import java.util.ArrayList;\n public class Example {\n public static void main(String[] args) {\n System.out.println(\"Hello World!\");\n }\n }' After the quick fix is applied: 'public class Example {\n public static void main(String[] args) {\n System.out.println(\"Hello World!\");\n }\n }'", + "markdown": "Reports redundant `import` statements.\n\nRegular `import` statements are unnecessary when not using imported classes and packages in the source file.\nThe same applies to imported `static` fields and methods that aren't used in the source file.\n\n**Example:**\n\n\n import java.util.ArrayList;\n public class Example {\n public static void main(String[] args) {\n System.out.println(\"Hello World!\");\n }\n }\n\nAfter the quick fix is applied:\n\n\n public class Example {\n public static void main(String[] args) {\n System.out.println(\"Hello World!\");\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UNUSED_IMPORT", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Imports", + "index": 22, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BooleanVariableAlwaysNegated", + "shortDescription": { + "text": "Boolean variable is always inverted" + }, + "fullDescription": { + "text": "Reports boolean variables or fields which are always negated when their value is used. Example: 'void m() {\n boolean b = true; //boolean variable 'b' is always inverted\n System.out.println(!b);\n }'", + "markdown": "Reports boolean variables or fields which are always negated when their value is used.\n\nExample:\n\n\n void m() {\n boolean b = true; //boolean variable 'b' is always inverted\n System.out.println(!b);\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "BooleanVariableAlwaysNegated", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Data flow", + "index": 23, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ExtendsAnnotation", + "shortDescription": { + "text": "Class extends annotation interface" + }, + "fullDescription": { + "text": "Reports classes declared as an implementation or extension of an annotation interface. While it is legal to extend an annotation interface, it is often done by accident, and the result can't be used as an annotation.", + "markdown": "Reports classes declared as an implementation or extension of an annotation interface.\n\nWhile it is legal to extend an annotation interface, it is often done by accident,\nand the result can't be used as an annotation." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ClassExplicitlyAnnotation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Inheritance issues", + "index": 25, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FieldAccessedSynchronizedAndUnsynchronized", + "shortDescription": { + "text": "Field accessed in both 'synchronized' and unsynchronized contexts" + }, + "fullDescription": { + "text": "Reports non-final fields that are accessed in both 'synchronized' and non-'synchronized' contexts. 'volatile' fields as well as accesses in constructors and initializers are ignored by this inspection. Such \"partially synchronized\" access is often the result of a coding oversight and may lead to unexpectedly inconsistent data structures. Example: 'public class Program {\n Console console; // warning: Field 'console' is accessed in both synchronized and unsynchronized contexts\n\n public synchronized void execute() {\n console.print(\"running\");\n }\n\n public void check() {\n console.check();\n }\n }'\n Use the option to specify if simple getters and setters are counted as accesses too.", + "markdown": "Reports non-final fields that are accessed in both `synchronized` and non-`synchronized` contexts. `volatile` fields as well as accesses in constructors and initializers are ignored by this inspection.\n\n\nSuch \"partially synchronized\" access is often the result of a coding oversight\nand may lead to unexpectedly inconsistent data structures.\n\n**Example:**\n\n\n public class Program {\n Console console; // warning: Field 'console' is accessed in both synchronized and unsynchronized contexts\n\n public synchronized void execute() {\n console.print(\"running\");\n }\n\n public void check() {\n console.check();\n }\n }\n\n\nUse the option to specify if simple getters and setters are counted as accesses too." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "FieldAccessedSynchronizedAndUnsynchronized", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NegatedEqualityExpression", + "shortDescription": { + "text": "Negated equality expression" + }, + "fullDescription": { + "text": "Reports equality expressions which are negated by a prefix expression. Such expressions can be simplified using the '!=' operator. Example: '!(i == 1)' After the quick-fix is applied: 'i != 1'", + "markdown": "Reports equality expressions which are negated by a prefix expression.\n\nSuch expressions can be simplified using the `!=` operator.\n\nExample:\n\n\n !(i == 1)\n\nAfter the quick-fix is applied:\n\n\n i != 1\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NegatedEqualityExpression", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RemoveLiteralUnderscores", + "shortDescription": { + "text": "Underscores in numeric literal" + }, + "fullDescription": { + "text": "Reports numeric literals with underscores and suggests removing them with a quick-fix. This may be useful if you need to lower the language level. The quick-fix removes underscores from numeric literals. For example '1_000_000' will be converted to '1000000'. Numeric literals with underscores appeared in Java 7. This inspection can help to downgrade for backward compatibility with earlier Java versions. New in 2020.2", + "markdown": "Reports numeric literals with underscores and suggests removing them with a quick-fix. This may be useful if you need to lower the language level.\n\nThe quick-fix removes underscores from numeric literals. For example `1_000_000` will be converted to `1000000`.\n\n\n*Numeric literals with underscores* appeared in Java 7.\nThis inspection can help to downgrade for backward compatibility with earlier Java versions.\n\nNew in 2020.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RemoveLiteralUnderscores", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MathRandomCastToInt", + "shortDescription": { + "text": "'Math.random()' cast to 'int'" + }, + "fullDescription": { + "text": "Reports calls to 'Math.random()' which are immediately cast to 'int'. Casting a 'double' between '0.0' (inclusive) and '1.0' (exclusive) to 'int' will always round down to zero. The value should first be multiplied by some factor before casting it to an 'int' to get a value between zero (inclusive) and the multiplication factor (exclusive). Another possible solution is to use the 'nextInt()' method of 'java.util.Random'. Example: 'int r = (int)Math.random() * 10;' After the quick fix is applied: 'int r = (int)(Math.random() * 10);'", + "markdown": "Reports calls to `Math.random()` which are immediately cast to `int`.\n\nCasting a `double` between `0.0` (inclusive) and\n`1.0` (exclusive) to `int` will always round down to zero. The value\nshould first be multiplied by some factor before casting it to an `int` to\nget a value between zero (inclusive) and the multiplication factor (exclusive).\nAnother possible solution is to use the `nextInt()` method of\n`java.util.Random`.\n\n**Example:**\n\n int r = (int)Math.random() * 10;\n\nAfter the quick fix is applied:\n\n int r = (int)(Math.random() * 10);\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "MathRandomCastToInt", + "cweIds": [ + 330, + 681 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DoubleBraceInitialization", + "shortDescription": { + "text": "Double brace initialization" + }, + "fullDescription": { + "text": "Reports Double Brace Initialization. Double brace initialization may cause memory leaks when used in a non-static context because it creates an anonymous class that will reference the surrounding object. Compared to regular initialization, double brace initialization provides worse performance since it requires loading an additional class. It may also cause failure of 'equals()' comparisons if the 'equals()' method doesn't accept subclasses as parameters. In addition, before Java 9, double brace initialization couldn't be combined with the diamond operator since it was incompatible with anonymous classes. Example: 'List list = new ArrayList<>() {{\n add(1);\n add(2);\n }};' After the quick-fix is applied: 'List list = new ArrayList<>();\n list.add(1);\n list.add(2);'", + "markdown": "Reports [Double Brace Initialization](https://www.c2.com/cgi/wiki?DoubleBraceInitialization).\n\nDouble brace initialization may cause memory leaks when used in a non-static context because it creates an anonymous class\nthat will reference the surrounding object.\n\nCompared to regular initialization, double brace initialization provides worse performance since it requires loading an\nadditional class.\n\nIt may also cause failure of `equals()` comparisons if the `equals()` method doesn't accept subclasses as\nparameters.\n\nIn addition, before Java 9, double brace initialization couldn't be combined with the diamond operator since it was incompatible\nwith anonymous classes.\n\n**Example:**\n\n\n List list = new ArrayList<>() {{\n add(1);\n add(2);\n }};\n\nAfter the quick-fix is applied:\n\n\n List list = new ArrayList<>();\n list.add(1);\n list.add(2);\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "DoubleBraceInitialization", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Initialization", + "index": 28, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringConcatenationInLoops", + "shortDescription": { + "text": "String concatenation in loop" + }, + "fullDescription": { + "text": "Reports String concatenation in loops. As every String concatenation copies the whole string, usually it is preferable to replace it with explicit calls to 'StringBuilder.append()' or 'StringBuffer.append()'. Example: 'String str = \"\";\n for(int i=0; i<10; i++) {\n str += i;\n }' After the quick-fix is applied: 'String str = \"\";\n StringBuilder strBuilder = new StringBuilder(str);\n for(int i = 0; i<10; i++) {\n strBuilder.append(i);\n }\n str = strBuilder.toString();' Sometimes, the quick-fixes allow you to convert a 'String' variable to a 'StringBuilder' or introduce a new 'StringBuilder'. Be careful if the original code specially handles the 'null' value, as the replacement may change semantics. If 'null' is possible, null-safe fixes that generate necessary null-checks are suggested. Also, it's not guaranteed that the automatic replacement will always be more performant.", + "markdown": "Reports String concatenation in loops.\n\n\nAs every String concatenation copies the whole\nstring, usually it is preferable to replace it with explicit calls to `StringBuilder.append()` or\n`StringBuffer.append()`.\n\n**Example:**\n\n\n String str = \"\";\n for(int i=0; i<10; i++) {\n str += i;\n }\n\nAfter the quick-fix is applied:\n\n\n String str = \"\";\n StringBuilder strBuilder = new StringBuilder(str);\n for(int i = 0; i<10; i++) {\n strBuilder.append(i);\n }\n str = strBuilder.toString();\n\n\nSometimes, the quick-fixes allow you to convert a `String` variable to a `StringBuilder` or\nintroduce a new `StringBuilder`. Be careful if the original code specially handles the `null` value, as the\nreplacement may change semantics. If `null` is possible, null-safe fixes that generate\nnecessary null-checks are suggested. Also, it's not guaranteed that the automatic replacement will always be more performant." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "StringConcatenationInLoop", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "unused", + "shortDescription": { + "text": "Unused declaration" + }, + "fullDescription": { + "text": "Reports classes, methods, or fields that are not used or unreachable from the entry points. An entry point can be a main method, tests, classes from outside the specified scope, classes accessible from 'module-info.java', and so on. It is possible to configure custom entry points by using name patterns or annotations. Example: 'public class Department {\n private Organization myOrganization;\n }' In this example, 'Department' explicitly references 'Organization' but if 'Department' class itself is unused, then inspection will report both classes. The inspection also reports parameters that are not used by their methods and all method implementations and overriders, as well as local variables that are declared but not used. Note: Some unused members may not be reported during in-editor code highlighting. For performance reasons, a non-private member is checked only when its name rarely occurs in the project. To see all results, run the inspection by selecting Code | Inspect Code or Code | Analyze Code | Run Inspection by Name from the main menu. Use the visibility settings below to configure members to be reported. For example, configuring report 'private' methods only means that 'public' methods of 'private' inner class will be reported but 'protected' methods of top level class will be ignored. Use the entry points tab to configure entry points to be considered during the inspection run. You can add entry points manually when inspection results are ready. If your code uses unsupported frameworks, there are several options: If the framework relies on annotations, use the Annotations... button to configure the framework's annotations. If the framework doesn't rely on annotations, try to configure class name patterns that are expected by the framework. This way the annotated code accessible by the framework internals will be treated as used.", + "markdown": "Reports classes, methods, or fields that are not used or unreachable from the entry points.\n\nAn entry point can be a main method, tests, classes from outside the specified scope, classes accessible from\n`module-info.java`, and so on. It is possible to configure custom entry points by using name patterns or annotations.\n\n**Example:**\n\n\n public class Department {\n private Organization myOrganization;\n }\n\nIn this example, `Department` explicitly references `Organization` but if `Department` class itself is unused, then inspection will report both classes.\n\n\nThe inspection also reports parameters that are not used by their methods and all method implementations and overriders, as well as local\nvariables that are declared but not used.\n\n\n**Note:** Some unused members may not be reported during in-editor code highlighting. For performance reasons, a non-private member is\nchecked only when its name rarely occurs in the project.\nTo see all results, run the inspection by selecting **Code \\| Inspect Code** or **Code \\| Analyze Code \\| Run Inspection by Name** from the main menu.\n\nUse the visibility settings below to configure members to be reported. For example, configuring report `private` methods only means\nthat `public` methods of `private` inner class will be reported but `protected` methods of top level class\nwill be ignored.\n\n\nUse the **entry points** tab to configure entry points to be considered during the inspection run.\n\nYou can add entry points manually when inspection results are ready.\n\nIf your code uses unsupported frameworks, there are several options:\n\n* If the framework relies on annotations, use the **Annotations...** button to configure the framework's annotations.\n* If the framework doesn't rely on annotations, try to configure class name patterns that are expected by the framework.\n\nThis way the annotated code accessible by the framework internals will be treated as used." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "unused", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CloneableClassInSecureContext", + "shortDescription": { + "text": "Cloneable class in secure context" + }, + "fullDescription": { + "text": "Reports classes which may be cloned. A class may be cloned if it supports the 'Cloneable' interface, and its 'clone()' method is not defined to immediately throw an error. Cloneable classes may be dangerous in code intended for secure use. Example: 'class SecureBean implements Cloneable {}' After the quick-fix is applied: 'class SecureBean {}' When the class extends an existing cloneable class or implements a cloneable interface, then after the quick-fix is applied, the code may look like: 'class SecureBean extends ParentBean {\n @Override\n protected SecureBean clone() throws CloneNotSupportedException {\n throw new CloneNotSupportedException();\n }\n}'", + "markdown": "Reports classes which may be cloned.\n\n\nA class\nmay be cloned if it supports the `Cloneable` interface,\nand its `clone()` method is not defined to immediately\nthrow an error. Cloneable classes may be dangerous in code intended for secure use.\n\n**Example:**\n`class SecureBean implements Cloneable {}`\n\nAfter the quick-fix is applied:\n`class SecureBean {}`\n\n\nWhen the class extends an existing cloneable class or implements a cloneable interface,\nthen after the quick-fix is applied, the code may look like:\n\n class SecureBean extends ParentBean {\n @Override\n protected SecureBean clone() throws CloneNotSupportedException {\n throw new CloneNotSupportedException();\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CloneableClassInSecureContext", + "cweIds": [ + 498 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Security", + "index": 30, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InconsistentTextBlockIndent", + "shortDescription": { + "text": "Inconsistent whitespace indentation in text block" + }, + "fullDescription": { + "text": "Reports text blocks that are indented using both spaces and tabs. Such cases produce unexpected results since spaces and tabs are treated equally by the text block processing. In the following example, spaces and tabs are visualized as '·' and '␉' respectively, and a tab is equal to 4 spaces in the editor. Example: 'String colors = \"\"\"\n········red\n␉ ␉ green\n········blue\"\"\";' After printing such a string, the result will be: '······red\ngreen\n······blue' After the compiler removes an equal amount of spaces or tabs from the beginning of each line, some lines remain with leading spaces. This inspection only reports if the configured language level is 15 or higher. New in 2021.1", + "markdown": "Reports text blocks that are indented using both spaces and tabs. Such cases produce unexpected results since spaces and tabs are treated equally by the text block processing.\n\nIn the following example, spaces and tabs are visualized as `·` and `␉` respectively,\nand a tab is equal to 4 spaces in the editor.\n\n**Example:**\n\n\n String colors = \"\"\"\n ········red\n ␉ ␉ green\n ········blue\"\"\";\n\nAfter printing such a string, the result will be:\n\n\n ······red\n green\n ······blue\n\nAfter the compiler removes an equal amount of spaces or tabs from the beginning of each line,\nsome lines remain with leading spaces.\n\nThis inspection only reports if the configured language level is 15 or higher.\n\nNew in 2021.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "InconsistentTextBlockIndent", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MismatchedStringCase", + "shortDescription": { + "text": "Mismatched case in 'String' operation" + }, + "fullDescription": { + "text": "Reports 'String' method calls that always return the same value ('-1' or 'false') because a lowercase character is searched in an uppercase-only string or vice versa. Reported methods include 'equals', 'startsWith', 'endsWith', 'contains', 'indexOf', and 'lastIndexOf'. Example: if (columnName.toLowerCase().equals(\"ID\")) {...}\n New in 2019.3", + "markdown": "Reports `String` method calls that always return the same value (`-1` or `false`) because a lowercase character is searched in an uppercase-only string or vice versa.\n\nReported methods include `equals`, `startsWith`, `endsWith`, `contains`,\n`indexOf`, and `lastIndexOf`.\n\n**Example:**\n\n```\n if (columnName.toLowerCase().equals(\"ID\")) {...}\n```\n\nNew in 2019.3" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "MismatchedStringCase", + "cweIds": [ + 597 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspiciousGetterSetter", + "shortDescription": { + "text": "Suspicious getter/setter" + }, + "fullDescription": { + "text": "Reports getter or setter methods that access a field that is not expected by its name. For example, when 'getY()' returns the 'x' field. Usually, it might be a copy-paste error. Example: 'class Point {\n private int x;\n private int y;\n\n public void setX(int x) { // Warning: setter 'setX()' assigns field 'y'\n this.y = x;\n }\n\n public int getY() { // Warning: getter 'getY()' returns field 'x'\n return x;\n }\n }' Use the checkbox below to report situations when a field in the class has a name that matches a name of a getter or a setter.", + "markdown": "Reports getter or setter methods that access a field that is not expected by its name. For example, when `getY()` returns the `x` field. Usually, it might be a copy-paste error.\n\n**Example:**\n\n class Point {\n private int x;\n private int y;\n\n public void setX(int x) { // Warning: setter 'setX()' assigns field 'y'\n this.y = x;\n }\n\n public int getY() { // Warning: getter 'getY()' returns field 'x'\n return x;\n }\n }\n\n\nUse the checkbox below to report situations when a field in the class has a name that matches a name of a getter or a setter." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SuspiciousGetterSetter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/JavaBeans issues", + "index": 33, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DoubleNegation", + "shortDescription": { + "text": "Double negation" + }, + "fullDescription": { + "text": "Reports double negations that can be simplified. Example: 'if (!!functionCall()) {}' After the quick-fix is applied: 'if (functionCall()) {}' Example: 'if (!(a != b)) {}' After the quick-fix is applied: 'if (a == b) {}'", + "markdown": "Reports double negations that can be simplified.\n\nExample:\n\n\n if (!!functionCall()) {}\n\nAfter the quick-fix is applied:\n\n\n if (functionCall()) {}\n\nExample:\n\n\n if (!(a != b)) {}\n\nAfter the quick-fix is applied:\n\n\n if (a == b) {}\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "DoubleNegation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AssertionCanBeIf", + "shortDescription": { + "text": "Assertion can be replaced with 'if' statement" + }, + "fullDescription": { + "text": "Reports 'assert' statements and suggests replacing them with 'if' statements that throw 'java.lang.AssertionError'. Example: 'assert param != null;' After the quick-fix is applied: 'if (param == null) throw new AssertionError();'", + "markdown": "Reports `assert` statements and suggests replacing them with `if` statements that throw `java.lang.AssertionError`.\n\nExample:\n\n\n assert param != null;\n\nAfter the quick-fix is applied:\n\n\n if (param == null) throw new AssertionError();\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "AssertionCanBeIf", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AssignmentToNull", + "shortDescription": { + "text": "'null' assignment" + }, + "fullDescription": { + "text": "Reports variables that are assigned to 'null' outside a declaration. The main purpose of 'null' in Java is to denote uninitialized reference variables. In rare cases, assigning a variable explicitly to 'null' is useful to aid garbage collection. However, using 'null' to denote a missing, not specified, or invalid value or a not found element is considered bad practice and may make your code more prone to 'NullPointerExceptions'. Instead, consider defining a sentinel object with the intended semantics or use library types like 'Optional' to denote the absence of a value. Example: 'Integer convert(String s) {\n Integer value;\n try {\n value = Integer.parseInt(s);\n } catch (NumberFormatException e) {\n // Warning: null is used to denote an 'invalid value'\n value = null;\n }\n return value;\n }' Use the Ignore assignments to fields option to ignore assignments to fields.", + "markdown": "Reports variables that are assigned to `null` outside a declaration.\n\nThe main purpose of `null` in Java is to denote uninitialized\nreference variables. In rare cases, assigning a variable explicitly to `null`\nis useful to aid garbage collection. However, using `null` to denote a missing, not specified, or invalid value or a not\nfound element is considered bad practice and may make your code more prone to `NullPointerExceptions`.\nInstead, consider defining a sentinel object with the intended semantics\nor use library types like `Optional` to denote the absence of a value.\n\n**Example:**\n\n\n Integer convert(String s) {\n Integer value;\n try {\n value = Integer.parseInt(s);\n } catch (NumberFormatException e) {\n // Warning: null is used to denote an 'invalid value'\n value = null;\n }\n return value;\n }\n\n\nUse the **Ignore assignments to fields** option to ignore assignments to fields." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AssignmentToNull", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Assignment issues", + "index": 34, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PackageWithTooFewClasses", + "shortDescription": { + "text": "Package with too few classes" + }, + "fullDescription": { + "text": "Reports packages that contain fewer classes than the specified minimum. Packages which contain subpackages are not reported. Overly small packages may indicate a fragmented design. Available only from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name and isn't reported in the editor. Use the Minimum number of classes field to specify the minimum allowed number of classes in a package.", + "markdown": "Reports packages that contain fewer classes than the specified minimum.\n\nPackages which contain subpackages are not reported. Overly small packages may indicate a fragmented design.\n\nAvailable only from **Code \\| Inspect Code** or\n**Code \\| Analyze Code \\| Run Inspection by Name** and isn't reported in the editor.\n\nUse the **Minimum number of classes** field to specify the minimum allowed number of classes in a package." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PackageWithTooFewClasses", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Packaging issues", + "index": 36, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceOnLiteralHasNoEffect", + "shortDescription": { + "text": "Replacement operation has no effect" + }, + "fullDescription": { + "text": "Reports calls to the 'String' methods 'replace()', 'replaceAll()' or 'replaceFirst()' that have no effect. Such calls can be guaranteed to have no effect when the qualifier and search string are compile-time constants and the search string is not found in the qualifier. This is redundant and may indicate an error. Example: '// replacement does nothing\n \"hello\".replace(\"$value$\", value);' New in 2022.1", + "markdown": "Reports calls to the `String` methods `replace()`, `replaceAll()` or `replaceFirst()` that have no effect. Such calls can be guaranteed to have no effect when the qualifier and search string are compile-time constants and the search string is not found in the qualifier. This is redundant and may indicate an error.\n\n**Example:**\n\n\n // replacement does nothing\n \"hello\".replace(\"$value$\", value);\n\nNew in 2022.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ReplaceOnLiteralHasNoEffect", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "WaitCalledOnCondition", + "shortDescription": { + "text": "'wait()' called on 'java.util.concurrent.locks.Condition' object" + }, + "fullDescription": { + "text": "Reports calls to 'wait()' made on a 'java.util.concurrent.locks.Condition' object. This is probably a programming error, and some variant of the 'await()' method was intended instead. Example: 'void acquire(Condition released) throws InterruptedException {\n while (acquired) {\n released.wait();\n }\n }' Good code would look like this: 'void acquire(Condition released) throws InterruptedException {\n while (acquired) {\n released.await();\n }\n }'", + "markdown": "Reports calls to `wait()` made on a `java.util.concurrent.locks.Condition` object. This is probably a programming error, and some variant of the `await()` method was intended instead.\n\n**Example:**\n\n\n void acquire(Condition released) throws InterruptedException {\n while (acquired) {\n released.wait();\n }\n }\n\nGood code would look like this:\n\n\n void acquire(Condition released) throws InterruptedException {\n while (acquired) {\n released.await();\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "WaitCalledOnCondition", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonSynchronizedMethodOverridesSynchronizedMethod", + "shortDescription": { + "text": "Unsynchronized method overrides 'synchronized' method" + }, + "fullDescription": { + "text": "Reports non-'synchronized' methods overriding 'synchronized' methods. The overridden method will not be automatically synchronized if the superclass method is declared as 'synchronized'. This may result in unexpected race conditions when using the subclass. Example: 'class Super {\n synchronized void process() {}\n }\n class Sub extends Super {\n // Unsynchronized method 'process()' overrides synchronized method\n void process() {}\n }'", + "markdown": "Reports non-`synchronized` methods overriding `synchronized` methods.\n\n\nThe overridden method will not be automatically synchronized if the superclass method\nis declared as `synchronized`. This may result in unexpected race conditions when using the subclass.\n\n**Example:**\n\n\n class Super {\n synchronized void process() {}\n }\n class Sub extends Super {\n // Unsynchronized method 'process()' overrides synchronized method\n void process() {}\n } \n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonSynchronizedMethodOverridesSynchronizedMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SingleClassImport", + "shortDescription": { + "text": "Single class import" + }, + "fullDescription": { + "text": "Reports 'import' statements that import single classes (as opposed to entire packages). Some coding standards prohibit such 'import' statements. You can configure IntelliJ IDEA to detect and fix such statements with its Optimize Imports command. Go to Settings | Editor | Code Style | Java | Imports and clear the Use single class import checkbox. Thus this inspection is mostly useful for offline reporting on code bases that you don't intend to change.", + "markdown": "Reports `import` statements that import single classes (as opposed to entire packages).\n\nSome coding standards prohibit such `import` statements.\n\n\nYou can configure IntelliJ IDEA to detect and fix such statements with its **Optimize Imports** command. Go to\n[Settings \\| Editor \\| Code Style \\| Java \\| Imports](settings://preferences.sourceCode.Java?Use%20single%20class%20import)\nand clear the **Use single class import** checkbox. Thus this inspection is mostly useful for\noffline reporting on code bases that you don't intend to change." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SingleClassImport", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Imports", + "index": 22, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TooBroadCatch", + "shortDescription": { + "text": "Overly broad 'catch' block" + }, + "fullDescription": { + "text": "Reports 'catch' blocks with parameters that are more generic than the exception thrown by the corresponding 'try' block. Example: 'try {\n File file = new File(pathToFile);\n return file.getAbsolutePath();\n } catch (Exception ex) { // warning: 'catch' of 'Exception' is too broad, masking exceptions 'RuntimeException'\n return defaultFilePath;\n }' After the quick-fix is applied: 'try {\n File file = new File(pathToFile);\n return file.getAbsolutePath();\n } catch (RuntimeException ex) {\n return defaultFilePath;\n }' Configure the inspection: Use the Only warn on RuntimeException, Exception, Error or Throwable option to have this inspection warn only on the most generic exceptions. Use the Ignore exceptions which hide others but are themselves thrown option to ignore any exceptions that hide other exceptions but still may be thrown and thus are technically not overly broad.", + "markdown": "Reports `catch` blocks with parameters that are more generic than the exception thrown by the corresponding `try` block.\n\n**Example:**\n\n\n try {\n File file = new File(pathToFile);\n return file.getAbsolutePath();\n } catch (Exception ex) { // warning: 'catch' of 'Exception' is too broad, masking exceptions 'RuntimeException'\n return defaultFilePath;\n }\n\nAfter the quick-fix is applied:\n\n\n try {\n File file = new File(pathToFile);\n return file.getAbsolutePath();\n } catch (RuntimeException ex) {\n return defaultFilePath;\n }\n\nConfigure the inspection:\n\n* Use the **Only warn on RuntimeException, Exception, Error or Throwable** option to have this inspection warn only on the most generic exceptions.\n* Use the **Ignore exceptions which hide others but are themselves thrown** option to ignore any exceptions that hide other exceptions but still may be thrown and thus are technically not overly broad." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OverlyBroadCatchBlock", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ProtectedMemberInFinalClass", + "shortDescription": { + "text": "'protected' member in 'final' class" + }, + "fullDescription": { + "text": "Reports 'protected' members in 'final'classes. Since 'final' classes cannot be inherited, marking the method as 'protected' may be confusing. It is better to declare such members as 'private' or package-visible instead. Example: 'record Bar(int a, int b) {\n protected int sum() { \n return a + b;\n }\n}'\n After the quick-fix is applied: 'record Bar(int a, int b) {\n int sum() { \n return a + b;\n }\n}' As shown in the example, a class can be marked as 'final' explicitly or implicitly.", + "markdown": "Reports `protected` members in `final`classes.\n\nSince `final` classes cannot be inherited, marking the method as `protected`\nmay be confusing. It is better to declare such members as `private` or package-visible instead.\n\n**Example:**\n\n record Bar(int a, int b) {\n protected int sum() { \n return a + b;\n }\n }\n\nAfter the quick-fix is applied:\n\n record Bar(int a, int b) {\n int sum() { \n return a + b;\n }\n }\n\nAs shown in the example, a class can be marked as `final` explicitly or implicitly." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ProtectedMemberInFinalClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DanglingJavadoc", + "shortDescription": { + "text": "Dangling Javadoc comment" + }, + "fullDescription": { + "text": "Reports Javadoc comments that don't belong to any class, method or field. The Javadoc tool ignores dangling Javadoc comments and doesn't include them in the HTML documentation it generates. Example: 'class A {\n /**\n * Dangling comment\n */\n /**\n * Method javadoc\n */\n public void m(){}\n }' A quick-fix is available to delete such comments completely or convert them into a block comment. After the quick-fix is applied: 'class A {\n /*\n Dangling comment\n */\n /**\n * Method javadoc\n */\n public void m(){}\n }' Use the Ignore file header comment in JavaDoc format option to ignore comments at the beginning of Java files. These are usually copyright messages.", + "markdown": "Reports Javadoc comments that don't belong to any class, method or field. The Javadoc tool ignores dangling Javadoc comments and doesn't include them in the HTML documentation it generates.\n\n**Example:**\n\n\n class A {\n /**\n * Dangling comment\n */\n /**\n * Method javadoc\n */\n public void m(){}\n }\n\nA quick-fix is available to delete such comments completely or convert them into a block comment. After the quick-fix is applied:\n\n\n class A {\n /*\n Dangling comment\n */\n /**\n * Method javadoc\n */\n public void m(){}\n }\n\nUse the **Ignore file header comment in JavaDoc format** option to ignore comments at the beginning of Java files.\nThese are usually copyright messages." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "DanglingJavadoc", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Javadoc", + "index": 45, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LoggingConditionDisagreesWithLogLevelStatement", + "shortDescription": { + "text": "Log condition does not match logging call" + }, + "fullDescription": { + "text": "Reports is log enabled for conditions of 'if' statements that do not match the log level of the contained logging call. For example: 'if (LOG.isTraceEnabled()) {\n // debug level logged, but checked for trace level\n LOG.debug(\"some log message\");\n }' This inspection understands the java.util.logging, Log4j, Log4j2, Apache Commons Logging and the SLF4J logging frameworks.", + "markdown": "Reports *is log enabled for* conditions of `if` statements that do not match the log level of the contained logging call.\n\n\nFor example:\n\n\n if (LOG.isTraceEnabled()) {\n // debug level logged, but checked for trace level\n LOG.debug(\"some log message\");\n }\n\nThis inspection understands the *java.util.logging* , *Log4j* , *Log4j2* , *Apache Commons Logging*\nand the *SLF4J* logging frameworks." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LoggingConditionDisagreesWithLogLevelStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages/Logging", + "index": 46, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BadOddness", + "shortDescription": { + "text": "Suspicious oddness check" + }, + "fullDescription": { + "text": "Reports odd-even checks of the following form: 'x % 2 == 1'. Such checks fail when used with negative odd values. Consider using 'x % 2 != 0' or '(x & 1) == 1' instead.", + "markdown": "Reports odd-even checks of the following form: `x % 2 == 1`. Such checks fail when used with negative odd values. Consider using `x % 2 != 0` or `(x & 1) == 1` instead." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "BadOddness", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "HibernateResource", + "shortDescription": { + "text": "Hibernate resource opened but not safely closed" + }, + "fullDescription": { + "text": "Reports calls to the 'openSession()' method if the returned 'org.hibernate.Session' resource is not safely closed. By default, the inspection assumes that the resources can be closed by any method with 'close' or 'cleanup' in its name. Example: 'void doHibernateQuery(SessionFactory factory) {\n Session session = factory.openSession(); //warning\n session.createQuery(\"...\");\n }' Use the following options to configure the inspection: Whether a 'org.hibernate.Session' resource is allowed to be opened inside a 'try' block. This style is less desirable because it is more verbose than opening a resource in front of a 'try' block. Whether the resource can be closed by any method call with the resource passed as argument.", + "markdown": "Reports calls to the `openSession()` method if the returned `org.hibernate.Session` resource is not safely closed.\n\n\nBy default, the inspection assumes that the resources can be closed by any method with\n'close' or 'cleanup' in its name.\n\n**Example:**\n\n\n void doHibernateQuery(SessionFactory factory) {\n Session session = factory.openSession(); //warning\n session.createQuery(\"...\");\n }\n\n\nUse the following options to configure the inspection:\n\n* Whether a `org.hibernate.Session` resource is allowed to be opened inside a `try` block. This style is less desirable because it is more verbose than opening a resource in front of a `try` block.\n* Whether the resource can be closed by any method call with the resource passed as argument." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "HibernateResourceOpenedButNotSafelyClosed", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Resource management", + "index": 47, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryInitCause", + "shortDescription": { + "text": "Unnecessary call to 'Throwable.initCause()'" + }, + "fullDescription": { + "text": "Reports calls to 'Throwable.initCause()' where an exception constructor also takes a 'Throwable cause' argument. In this case, the 'initCause()' call can be removed and its argument can be added to the call to the exception's constructor. Example: 'try {\n process();\n }\n catch (RuntimeException ex) {\n RuntimeException wrapper = new RuntimeException(\"Error while processing\");\n wrapper.initCause(ex); // Unnecessary call to 'Throwable.initCause()'\n throw wrapper;\n }' A quick-fix is available to pass the cause argument to the constructor. After the quick-fix is applied: 'try {\n process();\n }\n catch (RuntimeException ex) {\n RuntimeException wrapper = new RuntimeException(\"Error while processing\", ex);\n throw wrapper;\n }'", + "markdown": "Reports calls to `Throwable.initCause()` where an exception constructor also takes a `Throwable cause` argument.\n\nIn this case, the `initCause()` call can be removed and its argument can be added to the call to the exception's constructor.\n\n**Example:**\n\n\n try {\n process();\n }\n catch (RuntimeException ex) {\n RuntimeException wrapper = new RuntimeException(\"Error while processing\");\n wrapper.initCause(ex); // Unnecessary call to 'Throwable.initCause()'\n throw wrapper;\n }\n\nA quick-fix is available to pass the cause argument to the constructor. After the quick-fix is applied:\n\n\n try {\n process();\n }\n catch (RuntimeException ex) {\n RuntimeException wrapper = new RuntimeException(\"Error while processing\", ex);\n throw wrapper;\n }\n \n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryInitCause", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ComparisonOfShortAndChar", + "shortDescription": { + "text": "Comparison of 'short' and 'char' values" + }, + "fullDescription": { + "text": "Reports equality comparisons between 'short' and 'char' values. Such comparisons may cause subtle bugs because while both values are 2-byte long, 'short' values are signed, and 'char' values are unsigned. Example: 'if (Character.MAX_VALUE == shortValue()) {} //never can be true'", + "markdown": "Reports equality comparisons between `short` and `char` values.\n\nSuch comparisons may cause subtle bugs because while both values are 2-byte long, `short` values are\nsigned, and `char` values are unsigned.\n\n**Example:**\n\n\n if (Character.MAX_VALUE == shortValue()) {} //never can be true\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ComparisonOfShortAndChar", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ArrayCanBeReplacedWithEnumValues", + "shortDescription": { + "text": "Array can be replaced with enum values" + }, + "fullDescription": { + "text": "Reports arrays of enum constants that can be replaced with a call to 'EnumType.values()'. Usually, when updating such an enum, you have to update the array as well. However, if you use 'EnumType.values()' instead, no modifications are required. Example: 'enum States {\n NOT_RUN, IN_PROGRESS, FINISHED;\n }\n \n handleStates(new States[] {NOT_RUN, IN_PROGRESS, FINISHED});' After the quick-fix is applied: 'handleStates(States.values());' New in 2019.1", + "markdown": "Reports arrays of enum constants that can be replaced with a call to `EnumType.values()`.\n\nUsually, when updating such an enum, you have to update the array as well. However, if you use `EnumType.values()`\ninstead, no modifications are required.\n\nExample:\n\n\n enum States {\n NOT_RUN, IN_PROGRESS, FINISHED;\n }\n \n handleStates(new States[] {NOT_RUN, IN_PROGRESS, FINISHED});\n\nAfter the quick-fix is applied:\n\n\n handleStates(States.values());\n\nNew in 2019.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ArrayCanBeReplacedWithEnumValues", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "WaitNotifyNotInSynchronizedContext", + "shortDescription": { + "text": "'wait()' or 'notify()' is not in synchronized context" + }, + "fullDescription": { + "text": "Reports calls to 'wait()', 'notify()', and 'notifyAll()' that are not made inside a corresponding synchronized statement or synchronized method. Calling these methods on an object without holding a lock on that object causes 'IllegalMonitorStateException'. Such a construct is not necessarily an error, as the necessary lock may be acquired before the containing method is called, but it's worth looking at. Example: 'class Sync {\n private final Object lock = new Object();\n\n void test() throws InterruptedException {\n synchronized (this) {\n lock.wait(); // 'lock.wait()' is not synchronized on 'lock'\n }\n }\n }'", + "markdown": "Reports calls to `wait()`, `notify()`, and `notifyAll()` that are not made inside a corresponding synchronized statement or synchronized method.\n\n\nCalling these methods on an object\nwithout holding a lock on that object causes `IllegalMonitorStateException`.\nSuch a construct is not necessarily an error, as the necessary lock may be acquired before\nthe containing method is called, but it's worth looking at.\n\n**Example:**\n\n\n class Sync {\n private final Object lock = new Object();\n\n void test() throws InterruptedException {\n synchronized (this) {\n lock.wait(); // 'lock.wait()' is not synchronized on 'lock'\n }\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "WaitNotifyWhileNotSynced", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SystemOutErr", + "shortDescription": { + "text": "Use of 'System.out' or 'System.err'" + }, + "fullDescription": { + "text": "Reports usages of 'System.out' or 'System.err'. Such statements are often used for temporary debugging and should be either removed from the production code, or replaced by a more robust logging facility.", + "markdown": "Reports usages of `System.out` or `System.err`.\n\nSuch statements are often used for temporary debugging and should be either removed from the production code, or replaced by a more robust\nlogging facility." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UseOfSystemOutOrSystemErr", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code maturity", + "index": 50, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DollarSignInName", + "shortDescription": { + "text": "Use of '$' in identifier" + }, + "fullDescription": { + "text": "Reports variables, methods, and classes with dollar signs ('$') in their names. While such names are legal Java, their use outside of generated java code is strongly discouraged. Example: 'class SalaryIn${}' Rename quick-fix is suggested only in the editor.", + "markdown": "Reports variables, methods, and classes with dollar signs (`$`) in their names. While such names are legal Java, their use outside of generated java code is strongly discouraged.\n\n**Example:**\n\n\n class SalaryIn${}\n\nRename quick-fix is suggested only in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "DollarSignInName", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions", + "index": 51, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CheckedExceptionClass", + "shortDescription": { + "text": "Checked exception class" + }, + "fullDescription": { + "text": "Reports checked exception classes (that is, subclasses of 'java.lang.Exception' that are not subclasses of 'java.lang.RuntimeException'). Some coding standards suppress checked user-defined exception classes. Example: 'class IllegalMoveException extends Exception {}'", + "markdown": "Reports checked exception classes (that is, subclasses of `java.lang.Exception` that are not subclasses of `java.lang.RuntimeException`).\n\nSome coding standards suppress checked user-defined exception classes.\n\n**Example:**\n\n\n class IllegalMoveException extends Exception {}\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CheckedExceptionClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DynamicRegexReplaceableByCompiledPattern", + "shortDescription": { + "text": "Dynamic regular expression could be replaced by compiled 'Pattern'" + }, + "fullDescription": { + "text": "Reports calls to the regular expression methods (such as 'matches()' or 'split()') of 'java.lang.String' using constant arguments. Such calls may be profitably replaced with a 'private static final Pattern' field so that the regular expression does not have to be compiled each time it is used. Example: 'text.replaceAll(\"abc\", replacement);' After the quick-fix is applied: 'private static final Pattern ABC = Pattern.compile(\"abc\", Pattern.LITERAL);\n ABC.matcher(text).replaceAll(Matcher.quoteReplacement(replacement));'", + "markdown": "Reports calls to the regular expression methods (such as `matches()` or `split()`) of `java.lang.String` using constant arguments.\n\n\nSuch calls may be profitably replaced with a `private static final Pattern` field\nso that the regular expression does not have to be compiled each time it is used.\n\n**Example:**\n\n\n text.replaceAll(\"abc\", replacement);\n\nAfter the quick-fix is applied:\n\n\n private static final Pattern ABC = Pattern.compile(\"abc\", Pattern.LITERAL);\n ABC.matcher(text).replaceAll(Matcher.quoteReplacement(replacement));\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "DynamicRegexReplaceableByCompiledPattern", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EmptyInitializer", + "shortDescription": { + "text": "Empty class initializer" + }, + "fullDescription": { + "text": "Reports empty class initializer blocks.", + "markdown": "Reports empty class initializer blocks." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EmptyClassInitializer", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SerializableStoresNonSerializable", + "shortDescription": { + "text": "'Serializable' object implicitly stores non-'Serializable' object" + }, + "fullDescription": { + "text": "Reports any references to local non-'Serializable' variables outside 'Serializable' lambdas, local and anonymous classes. When a local variable is referenced from an anonymous class, its value is stored in an implicit field of that class. The same happens for local classes and lambdas. If the variable is of a non-'Serializable' type, serialization will fail. Example: 'interface A extends Serializable {\n abstract void foo();\n }\n class B {}\n class C {\n void foo() {\n B b = new B();\n A a = new A() {\n @Override\n public void foo() {\n System.out.println(b); // warning\n }\n };\n }\n }'", + "markdown": "Reports any references to local non-`Serializable` variables outside `Serializable` lambdas, local and anonymous classes.\n\n\nWhen a local variable is referenced from an anonymous class, its value\nis stored in an implicit field of that class. The same happens\nfor local classes and lambdas. If the variable is of a\nnon-`Serializable` type, serialization will fail.\n\n**Example:**\n\n\n interface A extends Serializable {\n abstract void foo();\n }\n class B {}\n class C {\n void foo() {\n B b = new B();\n A a = new A() {\n @Override\n public void foo() {\n System.out.println(b); // warning\n }\n };\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SerializableStoresNonSerializable", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryBreak", + "shortDescription": { + "text": "Unnecessary 'break' statement" + }, + "fullDescription": { + "text": "Reports any unnecessary 'break' statements. An 'break' statement is unnecessary if no other statements are executed after it has been removed. Example: 'switch (e) {\n case A -> {\n System.out.println(\"A\");\n break; // reports 'break' statement is unnecessary\n }\n default -> {\n System.out.println(\"Default\");\n break; // reports 'break' statement is unnecessary\n }\n }'", + "markdown": "Reports any unnecessary `break` statements.\n\nAn `break` statement is unnecessary if no other statements are executed after it has been removed.\n\n**Example:**\n\n\n switch (e) {\n case A -> {\n System.out.println(\"A\");\n break; // reports 'break' statement is unnecessary\n }\n default -> {\n System.out.println(\"Default\");\n break; // reports 'break' statement is unnecessary\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryBreak", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InsertLiteralUnderscores", + "shortDescription": { + "text": "Unreadable numeric literal" + }, + "fullDescription": { + "text": "Reports long numeric literals without underscores and suggests adding them. Underscores make such literals easier to read. Example: '1000000' After the quick-fix is applied: '1_000_000' This inspection only reports if the language level of the project of module is 7 or higher. New in 2020.2", + "markdown": "Reports long numeric literals without underscores and suggests adding them. Underscores make such literals easier to read.\n\nExample:\n\n\n 1000000\n\nAfter the quick-fix is applied:\n\n\n 1_000_000\n\nThis inspection only reports if the language level of the project of module is 7 or higher.\n\nNew in 2020.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "InsertLiteralUnderscores", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BreakStatement", + "shortDescription": { + "text": "'break' statement" + }, + "fullDescription": { + "text": "Reports 'break' statements that are used in places other than at the end of a 'switch' statement branch. 'break' statements complicate refactoring and can be confusing. Example: 'void foo(List strs) {\n for (String str : strs) {\n if (str.contains(\"stop\")) break;\n handleStr(str);\n }\n}'", + "markdown": "Reports `break` statements that are used in places other than at the end of a `switch` statement branch.\n\n`break` statements complicate refactoring and can be confusing.\n\nExample:\n\n\n void foo(List strs) {\n for (String str : strs) {\n if (str.contains(\"stop\")) break;\n handleStr(str);\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "BreakStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RawUseOfParameterizedType", + "shortDescription": { + "text": "Raw use of parameterized class" + }, + "fullDescription": { + "text": "Reports generic classes with omitted type parameters. Such raw use of generic types is valid in Java, but it defeats the purpose of type parameters and may mask bugs. This inspection mirrors the 'rawtypes' warning of 'javac'. Examples: '//warning: Raw use of parameterized class 'List'\nList list = new ArrayList();\n//list of strings was created but integer is accepted as well\nlist.add(1);' '//no warning as it's impossible to provide type arguments during array creation\nIntFunction[]> fun = List[]::new;' Configure the inspection: Use the Ignore construction of new objects option to ignore raw types used in object construction. Use the Ignore type casts option to ignore raw types used in type casts. Use the Ignore where a type parameter would not compile option to ignore the cases when a type parameter fails to compile (for example, when creating an array or overriding a library method). Use the Ignore parameter types of overriding methods option to ignore type parameters used in parameters of overridden methods. Use the Ignore when automatic quick-fix is not available option to ignore the cases when a quick-fix is not available. This inspection only reports if the language level of the project or module is 5 or higher.", + "markdown": "Reports generic classes with omitted type parameters. Such *raw* use of generic types is valid in Java, but it defeats the purpose of type parameters and may mask bugs. This inspection mirrors the `rawtypes` warning of `javac`.\n\n**Examples:**\n\n\n //warning: Raw use of parameterized class 'List'\n List list = new ArrayList();\n //list of strings was created but integer is accepted as well\n list.add(1);\n\n\n //no warning as it's impossible to provide type arguments during array creation\n IntFunction[]> fun = List[]::new;\n\nConfigure the inspection:\n\n* Use the **Ignore construction of new objects** option to ignore raw types used in object construction.\n* Use the **Ignore type casts** option to ignore raw types used in type casts.\n* Use the **Ignore where a type parameter would not compile** option to ignore the cases when a type parameter fails to compile (for example, when creating an array or overriding a library method).\n* Use the **Ignore parameter types of overriding methods** option to ignore type parameters used in parameters of overridden methods.\n* Use the **Ignore when automatic quick-fix is not available** option to ignore the cases when a quick-fix is not available.\nThis inspection only reports if the language level of the project or module is 5 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "rawtypes", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 5", + "index": 53, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JDBCExecuteWithNonConstantString", + "shortDescription": { + "text": "Call to 'Statement.execute()' with non-constant string" + }, + "fullDescription": { + "text": "Reports calls to 'java.sql.Statement.execute()' or any of its variants which take a dynamically-constructed string as the query to execute. Constructed SQL statements are a common source of security breaches. By default, this inspection ignores compile-time constants. Example: 'ResultSet execute(Statement statement, String name) throws SQLException {\n return statement.executeQuery(\"select * from \" + name); // reports warning\n }' Use the inspection options to consider any 'static' 'final' fields as constant. Be careful, because strings like the following will be ignored when the option is enabled: 'private static final String SQL = \"SELECT * FROM user WHERE name='\" + getUserInput() + \"'\";'", + "markdown": "Reports calls to `java.sql.Statement.execute()` or any of its variants which take a dynamically-constructed string as the query to execute.\n\nConstructed SQL statements are a common source of security breaches.\nBy default, this inspection ignores compile-time constants.\n\n**Example:**\n\n\n ResultSet execute(Statement statement, String name) throws SQLException {\n return statement.executeQuery(\"select * from \" + name); // reports warning\n }\n\n\nUse the inspection options to consider any `static` `final` fields as constant.\nBe careful, because strings like the following will be ignored when the option is enabled:\n\n\n private static final String SQL = \"SELECT * FROM user WHERE name='\" + getUserInput() + \"'\";\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "JDBCExecuteWithNonConstantString", + "cweIds": [ + 89, + 564 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Security", + "index": 30, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PublicConstructorInNonPublicClass", + "shortDescription": { + "text": "'public' constructor in non-public class" + }, + "fullDescription": { + "text": "Reports 'public' constructors in non-'public' classes. Usually, there is no reason for creating a 'public' constructor in a class with a lower access level. Please note, however, that this inspection changes the behavior of some reflection calls. In particular, 'Class.getConstructor()' won't be able to find the updated constructor ('Class.getDeclaredConstructor()' should be used instead). Do not use the inspection if your code or code of some used frameworks relies on constructor accessibility via 'getConstructor()'. Example: 'class House {\n public House() {}\n }' After the quick-fix is applied: 'class House {\n House() {}\n }'", + "markdown": "Reports `public` constructors in non-`public` classes.\n\nUsually, there is no reason for creating a `public` constructor in a class with a lower access level.\nPlease note, however, that this inspection changes the behavior of some reflection calls. In particular,\n`Class.getConstructor()` won't be able to find the updated constructor\n(`Class.getDeclaredConstructor()` should be used instead). Do not use the inspection if your code\nor code of some used frameworks relies on constructor accessibility via `getConstructor()`.\n\n**Example:**\n\n\n class House {\n public House() {}\n }\n\nAfter the quick-fix is applied:\n\n\n class House {\n House() {}\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PublicConstructorInNonPublicClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Visibility", + "index": 57, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConstantValueVariableUse", + "shortDescription": { + "text": "Use of variable whose value is known to be constant" + }, + "fullDescription": { + "text": "Reports any usages of variables which are known to be constant. This is the case if the (read) use of the variable is surrounded by an 'if', 'while', or 'for' statement with an '==' condition which compares the variable with a constant. In this case, the use of a variable which is known to be constant can be replaced with an actual constant. Example: 'private static void foo(double number) {\n if (number == 1.0) {\n f(number);\n }\n }\n private static void f(double number) {}' After the quick-fix is applied: 'private static void foo(double number) {\n if (number == 1.0) {\n f(1.0);\n }\n }\n private static void f(double number) {}'", + "markdown": "Reports any usages of variables which are known to be constant.\n\nThis is the case if the (read) use of the variable is surrounded by an\n`if`, `while`, or `for`\nstatement with an `==` condition which compares the variable with a constant.\nIn this case, the use of a variable which is known to be constant can be replaced with\nan actual constant.\n\nExample:\n\n\n private static void foo(double number) {\n if (number == 1.0) {\n f(number);\n }\n }\n private static void f(double number) {}\n\nAfter the quick-fix is applied:\n\n\n private static void foo(double number) {\n if (number == 1.0) {\n f(1.0);\n }\n }\n private static void f(double number) {}\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ConstantValueVariableUse", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Data flow", + "index": 23, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NewStringBufferWithCharArgument", + "shortDescription": { + "text": "StringBuilder constructor call with 'char' argument" + }, + "fullDescription": { + "text": "Reports calls to 'StringBuffer' and 'StringBuilder' constructors with 'char' as the argument. In this case, 'char' is silently cast to an integer and interpreted as the initial capacity of the buffer. Example: 'new StringBuilder('(').append(\"1\").append(')');' After the quick-fix is applied: 'new StringBuilder(\"(\").append(\"1\").append(')');'", + "markdown": "Reports calls to `StringBuffer` and `StringBuilder` constructors with `char` as the argument. In this case, `char` is silently cast to an integer and interpreted as the initial capacity of the buffer.\n\n**Example:**\n\n\n new StringBuilder('(').append(\"1\").append(')');\n\nAfter the quick-fix is applied:\n\n\n new StringBuilder(\"(\").append(\"1\").append(')');\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "NewStringBufferWithCharArgument", + "cweIds": [ + 628, + 704 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ProtectedInnerClass", + "shortDescription": { + "text": "Protected nested class" + }, + "fullDescription": { + "text": "Reports 'protected' nested classes. Example: 'public class Outer {\n protected static class Nested {} // warning\n protected class Inner {} // warning\n protected enum Mode {} // warning depends on the setting\n protected interface I {} // warning depends on the setting\n }' Configure the inspection: Use the Ignore 'protected' inner enums option to ignore 'protected' inner enums. Use the Ignore 'protected' inner interfaces option to ignore 'protected' inner interfaces.", + "markdown": "Reports `protected` nested classes.\n\n**Example:**\n\n\n public class Outer {\n protected static class Nested {} // warning\n protected class Inner {} // warning\n protected enum Mode {} // warning depends on the setting\n protected interface I {} // warning depends on the setting\n }\n\nConfigure the inspection:\n\n* Use the **Ignore 'protected' inner enums** option to ignore `protected` inner enums.\n* Use the **Ignore 'protected' inner interfaces** option to ignore `protected` inner interfaces." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ProtectedInnerClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Encapsulation", + "index": 60, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnqualifiedStaticUsage", + "shortDescription": { + "text": "Unqualified static access" + }, + "fullDescription": { + "text": "Reports usage of static members that is not qualified with the class name. This is legal if the static member is in the same class, but may be confusing. Example: 'class Foo {\n static void foo() {}\n static int x;\n\n void bar() {\n foo();\n System.out.println(x);\n }\n\n static void baz() { foo(); }\n }' After the quick-fix is applied: 'class Foo {\n static void foo() {}\n static int x;\n\n void bar() {\n Foo.foo();\n System.out.println(Foo.x);\n }\n\n static void baz() { Foo.foo(); }\n }' Use the inspection settings to toggle the reporting for the following items: static fields access 'void bar() { System.out.println(x); }' calls to static methods 'void bar() { foo(); }' 'static void baz() { foo(); }' You can also configure the inspection to only report static member usage from a non-static context. In the above example, 'static void baz() { foo(); }' will not be reported.", + "markdown": "Reports usage of static members that is not qualified with the class name.\n\n\nThis is legal if the static member is in\nthe same class, but may be confusing.\n\n**Example:**\n\n\n class Foo {\n static void foo() {}\n static int x;\n\n void bar() {\n foo();\n System.out.println(x);\n }\n\n static void baz() { foo(); }\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo {\n static void foo() {}\n static int x;\n\n void bar() {\n Foo.foo();\n System.out.println(Foo.x);\n }\n\n static void baz() { Foo.foo(); }\n }\n\nUse the inspection settings to toggle the reporting for the following items:\n\n*\n static fields access \n\n `void bar() { System.out.println(x); }`\n\n*\n calls to static methods \n\n `void bar() { foo(); }` \n\n `static void baz() { foo(); }`\n\n\nYou can also configure the inspection to only report static member usage from a non-static context.\nIn the above example, `static void baz() { foo(); }` will not be reported." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnqualifiedStaticUsage", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ExternalizableWithoutPublicNoArgConstructor", + "shortDescription": { + "text": "'Externalizable' class without 'public' no-arg constructor" + }, + "fullDescription": { + "text": "Reports 'Externalizable' classes without a public no-argument constructor. When an 'Externalizable' object is reconstructed, an instance is created using the public no-arg constructor before the 'readExternal' method called. If a public no-arg constructor is not available, a 'java.io.InvalidClassException' will be thrown at runtime.", + "markdown": "Reports `Externalizable` classes without a public no-argument constructor.\n\nWhen an `Externalizable` object is reconstructed, an instance is created using the public\nno-arg constructor before the `readExternal` method called. If a public\nno-arg constructor is not available, a `java.io.InvalidClassException` will be\nthrown at runtime." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ExternalizableWithoutPublicNoArgConstructor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StaticGuardedByInstance", + "shortDescription": { + "text": "Static member guarded by instance field or this" + }, + "fullDescription": { + "text": "Reports '@GuardedBy' annotations on 'static' fields or methods in which the guard is either a non-static field or 'this'. Guarding a static element with a non-static element may result in excessive concurrency, multiple threads may be able to access the guarded field simultaneously by locking in different object contexts. Example: 'private ReadWriteLock lock = new ReentrantReadWriteLock();\n\n @GuardedBy(\"lock\")\n public static void bar() {\n // ...\n }' Supported '@GuardedBy' annotations are: 'net.jcip.annotations.GuardedBy' 'javax.annotation.concurrent.GuardedBy' 'org.apache.http.annotation.GuardedBy' 'com.android.annotations.concurrency.GuardedBy' 'androidx.annotation.GuardedBy' 'com.google.errorprone.annotations.concurrent.GuardedBy'", + "markdown": "Reports `@GuardedBy` annotations on `static` fields or methods in which the guard is either a non-static field or `this`.\n\nGuarding a static element with a non-static element may result in\nexcessive concurrency, multiple threads may be able to access the guarded field simultaneously by locking in different object contexts.\n\nExample:\n\n\n private ReadWriteLock lock = new ReentrantReadWriteLock();\n\n @GuardedBy(\"lock\")\n public static void bar() {\n // ...\n }\n\nSupported `@GuardedBy` annotations are:\n\n* `net.jcip.annotations.GuardedBy`\n* `javax.annotation.concurrent.GuardedBy`\n* `org.apache.http.annotation.GuardedBy`\n* `com.android.annotations.concurrency.GuardedBy`\n* `androidx.annotation.GuardedBy`\n* `com.google.errorprone.annotations.concurrent.GuardedBy`" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StaticGuardedByInstance", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Concurrency annotation issues", + "index": 61, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ResultOfObjectAllocationIgnored", + "shortDescription": { + "text": "Result of object allocation ignored" + }, + "fullDescription": { + "text": "Reports object allocations where the allocated object is ignored and neither assigned to a variable nor used in another way. Such allocation expressions are legal in Java, but are usually either unintended, or evidence of a very odd object initialization strategy. Use the options to list classes whose allocations should be ignored by this inspection.", + "markdown": "Reports object allocations where the allocated object is ignored and neither assigned to a variable nor used in another way.\n\n\nSuch allocation expressions are legal in Java, but are usually either unintended, or\nevidence of a very odd object initialization strategy.\n\n\nUse the options to list classes whose allocations should be ignored by this inspection." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ResultOfObjectAllocationIgnored", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassGetClass", + "shortDescription": { + "text": "Suspicious 'Class.getClass()' call" + }, + "fullDescription": { + "text": "Reports 'getClass()' methods that are called on a 'java.lang.Class' instance. This is usually a mistake as the result is always equivalent to 'Class.class'. If it's a mistake, then it's better to remove the 'getClass()' call and use the qualifier directly. If the behavior is intended, then it's better to write 'Class.class' explicitly to avoid confusion. Example: 'void test(Class clazz) {\n String name = clazz.getClass().getName();\n }' After one of the possible quick-fixes is applied: 'void test(Class clazz) {\n String name = clazz.getName();\n }' New in 2018.2", + "markdown": "Reports `getClass()` methods that are called on a `java.lang.Class` instance.\n\nThis is usually a mistake as the result is always equivalent to `Class.class`.\nIf it's a mistake, then it's better to remove the `getClass()` call and use the qualifier directly.\nIf the behavior is intended, then it's better to write `Class.class` explicitly to avoid confusion.\n\nExample:\n\n\n void test(Class clazz) {\n String name = clazz.getClass().getName();\n }\n\nAfter one of the possible quick-fixes is applied:\n\n\n void test(Class clazz) {\n String name = clazz.getName();\n }\n\nNew in 2018.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ClassGetClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnusedLibrary", + "shortDescription": { + "text": "Unused library" + }, + "fullDescription": { + "text": "Reports libraries attached to the specified inspection scope that are not used directly in code.", + "markdown": "Reports libraries attached to the specified inspection scope that are not used directly in code." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnusedLibrary", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ManualArrayCopy", + "shortDescription": { + "text": "Manual array copy" + }, + "fullDescription": { + "text": "Reports manual copying of array contents that can be replaced with a call to 'System.arraycopy()'. Example: 'for (int i = 0; i < array.length; i++) {\n newArray[i] = array[i];\n }' After the quick-fix is applied: 'System.arraycopy(array, 0, newArray, 0, array.length);'", + "markdown": "Reports manual copying of array contents that can be replaced with a call to `System.arraycopy()`.\n\n**Example:**\n\n\n for (int i = 0; i < array.length; i++) {\n newArray[i] = array[i];\n }\n\nAfter the quick-fix is applied:\n\n\n System.arraycopy(array, 0, newArray, 0, array.length);\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ManualArrayCopy", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ObsoleteCollection", + "shortDescription": { + "text": "Use of obsolete collection type" + }, + "fullDescription": { + "text": "Reports usages of 'java.util.Vector', 'java.util.Hashtable' and 'java.util.Stack'. Usages of these classes can often be replaced with usages of 'java.util.ArrayList', 'java.util.HashMap' and 'java.util.ArrayDeque' respectively. While still supported, the former classes were made obsolete by the JDK1.2 collection classes, and should probably not be used in new development. Use the Ignore obsolete collection types where they are required option to ignore any cases where the obsolete collections are used as method arguments or assigned to a variable that requires the obsolete type. Enabling this option may consume significant processor resources.", + "markdown": "Reports usages of `java.util.Vector`, `java.util.Hashtable` and `java.util.Stack`.\n\nUsages of these classes can often be replaced with usages of\n`java.util.ArrayList`, `java.util.HashMap` and `java.util.ArrayDeque` respectively.\nWhile still supported,\nthe former classes were made obsolete by the JDK1.2 collection classes, and should probably\nnot be used in new development.\n\n\nUse the **Ignore obsolete collection types where they are required** option to ignore any cases where the obsolete collections are used\nas method arguments or assigned to a variable that requires the obsolete type.\nEnabling this option may consume significant processor resources." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UseOfObsoleteCollectionType", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code maturity", + "index": 50, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StaticPseudoFunctionalStyleMethod", + "shortDescription": { + "text": "Guava pseudo-functional call can be converted to Stream API call" + }, + "fullDescription": { + "text": "Reports usages of Guava pseudo-functional code when 'Java Stream API' is available. Though 'Guava Iterable API' provides functionality similar to 'Java Streams API', it's slightly different and may miss some features. Especially, primitive-specialized stream variants like 'IntStream' are more performant than generic variants. Example: 'List transformedIterable = Iterables.transform(someList, someTransformFunction);//warning: Pseudo functional style code' After the quick-fix is applied: 'List transformedIterable = someList.stream().map(someTransformFunction).collect(Collectors.toList());' Note: Code semantics can be changed; for example, Guava's 'Iterable.transform' produces a lazy-evaluated iterable, but the replacement is eager-evaluated. This inspection only reports if the language level of the project or module is 8 or higher.", + "markdown": "Reports usages of Guava pseudo-functional code when `Java Stream API` is available.\n\nThough `Guava Iterable API` provides functionality similar to `Java Streams API`, it's slightly different and\nmay miss some features.\nEspecially, primitive-specialized stream variants like `IntStream` are more performant than generic variants.\n\n**Example:**\n\n\n List transformedIterable = Iterables.transform(someList, someTransformFunction);//warning: Pseudo functional style code\n\nAfter the quick-fix is applied:\n\n List transformedIterable = someList.stream().map(someTransformFunction).collect(Collectors.toList());\n\n\n**Note:** Code semantics can be changed; for example, Guava's `Iterable.transform` produces a lazy-evaluated iterable,\nbut the replacement is eager-evaluated.\n\nThis inspection only reports if the language level of the project or module is 8 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StaticPseudoFunctionalStyleMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 8", + "index": 62, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ControlFlowStatementWithoutBraces", + "shortDescription": { + "text": "Control flow statement without braces" + }, + "fullDescription": { + "text": "Reports any 'if', 'while', 'do', or 'for' statements without braces. Some code styles, e.g. the Google Java Style guide, require braces for all control statements. When adding further statements to control statements without braces, it is important not to forget adding braces. When commenting out a line of code, it is also necessary to be more careful when not using braces, to not inadvertently make the next statement part of the control flow statement. Always using braces makes inserting or commenting out a line of code safer. It's likely the goto fail vulnerability would not have happened, if an always use braces code style was used. Control statements with braces make the control flow easier to see, without relying on, possibly incorrect, indentation. Example: 'class Strange {\n void x(boolean one, boolean two) {\n if(one)\n if(two)\n foo();\n else\n bar();\n }\n\n void foo() {}\n void bar() {}\n }' The quick-fix wraps the statement body with braces: 'class Strange {\n void x(boolean one, boolean two) {\n if(one) {\n if(two) {\n foo();\n } else {\n bar();\n }\n }\n }\n\n void foo() {}\n void bar() {}\n }'", + "markdown": "Reports any `if`, `while`, `do`, or `for` statements without braces. Some code styles, e.g. the [Google Java Style guide](https://google.github.io/styleguide/javaguide.html), require braces for all control statements.\n\n\nWhen adding further statements to control statements without braces, it is important not to forget adding braces.\nWhen commenting out a line of code, it is also necessary to be more careful when not using braces,\nto not inadvertently make the next statement part of the control flow statement.\nAlways using braces makes inserting or commenting out a line of code safer.\n\n\nIt's likely the [goto fail vulnerability](https://www.imperialviolet.org/2014/02/22/applebug.html) would not have happened,\nif an always use braces code style was used.\nControl statements with braces make the control flow easier to see, without relying on, possibly incorrect, indentation.\n\nExample:\n\n\n class Strange {\n void x(boolean one, boolean two) {\n if(one)\n if(two)\n foo();\n else\n bar();\n }\n\n void foo() {}\n void bar() {}\n }\n\nThe quick-fix wraps the statement body with braces:\n\n\n class Strange {\n void x(boolean one, boolean two) {\n if(one) {\n if(two) {\n foo();\n } else {\n bar();\n }\n }\n }\n\n void foo() {}\n void bar() {}\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ControlFlowStatementWithoutBraces", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LengthOneStringInIndexOf", + "shortDescription": { + "text": "Single character string argument in 'String.indexOf()' call" + }, + "fullDescription": { + "text": "Reports single character strings being used as an argument in 'String.indexOf()' and 'String.lastIndexOf()' calls. A quick-fix is suggested to replace such string literals with equivalent character literals, gaining some performance enhancement. Example: 'return s.indexOf(\"x\");' After the quick-fix is applied: 'return s.indexOf('x');'", + "markdown": "Reports single character strings being used as an argument in `String.indexOf()` and `String.lastIndexOf()` calls.\n\nA quick-fix is suggested to replace such string literals with equivalent character literals, gaining some performance enhancement.\n\n**Example:**\n\n\n return s.indexOf(\"x\");\n\nAfter the quick-fix is applied:\n\n\n return s.indexOf('x');\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SingleCharacterStringConcatenation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MisspelledEquals", + "shortDescription": { + "text": "'equal()' instead of 'equals()'" + }, + "fullDescription": { + "text": "Reports declarations of 'equal()' with a single parameter. Normally, this is a typo and 'equals()' is actually intended. A quick-fix is suggested to rename the method to 'equals'. Example: 'class Main {\n public boolean equal(Object obj) {\n return true;\n }\n }' After the quick-fix is applied: 'class Main {\n public boolean equals(Object obj) {\n return true;\n }\n }'", + "markdown": "Reports declarations of `equal()` with a single parameter. Normally, this is a typo and `equals()` is actually intended.\n\nA quick-fix is suggested to rename the method to `equals`.\n\n**Example:**\n\n\n class Main {\n public boolean equal(Object obj) {\n return true;\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Main {\n public boolean equals(Object obj) {\n return true;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MisspelledEquals", + "cweIds": [ + 697 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonFinalFieldInEnum", + "shortDescription": { + "text": "Non-final field in 'enum'" + }, + "fullDescription": { + "text": "Reports non-final fields in enumeration types. Non-final fields introduce global mutable state, which is generally considered undesirable. Example: 'enum Enum {\n FIRST(\"first\"),\n SECOND(\"second\");\n\n public String str;\n\n Enum(String str) {\n this.str = str;\n }\n }' After the quick-fix is applied: 'enum Enum {\n FIRST(\"first\"),\n SECOND(\"second\");\n\n public final String str;\n\n Enum(String str) {\n this.str = str;\n }\n }' Use the `Ignore fields that cannot be made 'final'` option to only warn on fields that can be made final using the quick-fix.", + "markdown": "Reports non-final fields in enumeration types. Non-final fields introduce global mutable state, which is generally considered undesirable.\n\n**Example:**\n\n\n enum Enum {\n FIRST(\"first\"),\n SECOND(\"second\");\n\n public String str;\n\n Enum(String str) {\n this.str = str;\n }\n }\n\nAfter the quick-fix is applied:\n\n\n enum Enum {\n FIRST(\"first\"),\n SECOND(\"second\");\n\n public final String str;\n\n Enum(String str) {\n this.str = str;\n }\n }\n\nUse the \\`Ignore fields that cannot be made 'final'\\` option to only warn on fields that can be made final using the quick-fix." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonFinalFieldInEnum", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ForEachWithRecordPatternCanBeUsed", + "shortDescription": { + "text": "Enhanced 'for' with a record pattern can be used" + }, + "fullDescription": { + "text": "Reports local variable declarations and accessors to record components that can be replaced with pattern variables in enhanced `for` statements, which are usually more compact. Example: 'record Record(Integer x, String y) {\n}\n\npublic static void test(List records) {\n for (Record record : records) {\n System.out.println(record.y());\n Integer x = record.x;\n System.out.println(x);\n }\n}' Can be replaced with: 'record Record(Integer x, String y) {\n}\n\npublic static void test(List records) {\n for (Record(Integer x, String y) : records) {\n System.out.println(y);\n System.out.println(x);\n }\n}' This inspection only reports if the language level of the project or module is 20 or higher Use the Nesting depth limit option to specify the maximum number of nested deconstruction patterns to report Use the Maximum number of record components to deconstruct option to specify the maximum number of components, which a record can contain to be used in deconstruction patterns Use the Maximum number of not-used record components option to specify the maximum number of components, which are not used in 'for' statement New in 2023.1", + "markdown": "Reports local variable declarations and accessors to record components that can be replaced with pattern variables in enhanced \\`for\\` statements, which are usually more compact.\n\n**Example:**\n\n\n record Record(Integer x, String y) {\n }\n\n public static void test(List records) {\n for (Record record : records) {\n System.out.println(record.y());\n Integer x = record.x;\n System.out.println(x);\n }\n }\n\nCan be replaced with:\n\n\n record Record(Integer x, String y) {\n }\n\n public static void test(List records) {\n for (Record(Integer x, String y) : records) {\n System.out.println(y);\n System.out.println(x);\n }\n }\n\nThis inspection only reports if the language level of the project or module is 20 or higher\n\n* Use the **Nesting depth limit** option to specify the maximum number of nested deconstruction patterns to report\n* Use the **Maximum number of record components to deconstruct** option to specify the maximum number of components, which a record can contain to be used in deconstruction patterns\n* Use the **Maximum number of not-used record components** option to specify the maximum number of components, which are not used in `for` statement\n\nNew in 2023.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ForEachWithRecordPatternCanBeUsed", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 20", + "index": 63, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TrivialFunctionalExpressionUsage", + "shortDescription": { + "text": "Trivial usage of functional expression" + }, + "fullDescription": { + "text": "Reports functional interface methods calls that are directly invoked on the definition of the lambda, method reference, or anonymous class. Such method calls can be replaced with the body of the functional interface implementation. Example: 'boolean contains(List names, String name) {\n return ((Predicate)x -> {\n return names.contains(x);\n }).test(name);\n }' When the quick-fix is applied, the method call changes to: 'boolean contains(List names, String name) {\n return names.contains(name);\n }'", + "markdown": "Reports functional interface methods calls that are directly invoked on the definition of the lambda, method reference, or anonymous class. Such method calls can be replaced with the body of the functional interface implementation.\n\n**Example:**\n\n\n boolean contains(List names, String name) {\n return ((Predicate)x -> {\n return names.contains(x);\n }).test(name);\n }\n\nWhen the quick-fix is applied, the method call changes to:\n\n\n boolean contains(List names, String name) {\n return names.contains(name);\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "TrivialFunctionalExpressionUsage", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EnumClass", + "shortDescription": { + "text": "Enumerated class" + }, + "fullDescription": { + "text": "Reports enum classes. Such statements are not supported in Java 1.4 and earlier JVM.", + "markdown": "Reports **enum** classes. Such statements are not supported in Java 1.4 and earlier JVM." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EnumClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level issues", + "index": 64, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MismatchedStringBuilderQueryUpdate", + "shortDescription": { + "text": "Mismatched query and update of 'StringBuilder'" + }, + "fullDescription": { + "text": "Reports 'StringBuilder', 'StringBuffer' or 'StringJoiner' objects whose contents are read but not written to, or written to but not read. Such inconsistent reads and writes are pointless and probably indicate dead, incomplete, or erroneous code. Example: 'public void m1() {\n StringBuilder sb = new StringBuilder();\n sb.append(\"a\");\n }'", + "markdown": "Reports `StringBuilder`, `StringBuffer` or `StringJoiner` objects whose contents are read but not written to, or written to but not read.\n\nSuch inconsistent reads and writes are pointless and probably indicate\ndead, incomplete, or erroneous code.\n\n**Example:**\n\n\n public void m1() {\n StringBuilder sb = new StringBuilder();\n sb.append(\"a\");\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "MismatchedQueryAndUpdateOfStringBuilder", + "cweIds": [ + 561, + 563 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AnonymousHasLambdaAlternative", + "shortDescription": { + "text": "Anonymous type has shorter lambda alternative" + }, + "fullDescription": { + "text": "Reports anonymous classes which could be transformed to a constructor or a factory method call with a lambda expression argument. The following classes are reported by this inspection: Anonymous classes extending 'ThreadLocal' which have an 'initialValue()' method (can be replaced with 'ThreadLocal.withInitial') Anonymous classes extending 'Thread' which have a 'run()' method (can be replaced with 'new Thread(Runnable)' Example: 'new Thread() {\n @Override\n public void run() {\n System.out.println(\"Hello from thread!\");\n }\n }.start();' After the quick-fix is applied: 'new Thread(() -> {\n System.out.println(\"Hello from thread!\");\n }).start();'", + "markdown": "Reports anonymous classes which could be transformed to a constructor or a factory method call with a lambda expression argument.\n\nThe following classes are reported by this inspection:\n\n* Anonymous classes extending `ThreadLocal` which have an `initialValue()` method (can be replaced with `ThreadLocal.withInitial`)\n* Anonymous classes extending `Thread` which have a `run()` method (can be replaced with `new Thread(Runnable)`\n\nExample:\n\n\n new Thread() {\n @Override\n public void run() {\n System.out.println(\"Hello from thread!\");\n }\n }.start();\n\nAfter the quick-fix is applied:\n\n\n new Thread(() -> {\n System.out.println(\"Hello from thread!\");\n }).start();\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AnonymousHasLambdaAlternative", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 8", + "index": 62, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FinalizeNotProtected", + "shortDescription": { + "text": "'finalize()' should be protected, not public" + }, + "fullDescription": { + "text": "Reports any implementations of the 'Object.finalize()' method that are declared 'public'. According to the contract of the 'Object.finalize()', only the garbage collector calls this method. Making this method public may be confusing, because it means that the method can be used from other code. A quick-fix is provided to make the method 'protected', to prevent it from being invoked from other classes. Example: 'class X {\n public void finalize() {\n /* ... */\n }\n }' After the quick-fix is applied: 'class X {\n protected void finalize() {\n /* ... */\n }\n }'", + "markdown": "Reports any implementations of the `Object.finalize()` method that are declared `public`.\n\n\nAccording to the contract of the `Object.finalize()`, only the garbage\ncollector calls this method. Making this method public may be confusing, because it\nmeans that the method can be used from other code.\n\n\nA quick-fix is provided to make the method `protected`, to prevent it from being invoked\nfrom other classes.\n\n**Example:**\n\n\n class X {\n public void finalize() {\n /* ... */\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class X {\n protected void finalize() {\n /* ... */\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "FinalizeNotProtected", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Finalization", + "index": 66, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ExtendsThread", + "shortDescription": { + "text": "Class directly extends 'Thread'" + }, + "fullDescription": { + "text": "Reports classes that directly extend 'java.lang.Thread'. It is usually recommended to prefer composition over inheritance to create more reusable code that is easier to modify later. Example: 'class MainThread extends Thread {\n }'", + "markdown": "Reports classes that directly extend `java.lang.Thread`. It is usually recommended to prefer composition over inheritance to create more reusable code that is easier to modify later.\n\n**Example:**\n\n\n class MainThread extends Thread {\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassExplicitlyExtendsThread", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LogStatementGuardedByLogCondition", + "shortDescription": { + "text": "Logging call not guarded by log condition" + }, + "fullDescription": { + "text": "Reports logging calls with non-constant arguments that are not surrounded by a guard condition. The evaluation of the arguments of a logging call can be expensive. Surrounding a logging call with a guard clause prevents that cost when logging is disabled for the level used by the logging statement. This is especially useful for the least serious level (trace, debug, finest) of logging calls, because those are most often disabled in a production environment. Example: 'public class Principal {\n void bad(Object object) {\n if (true) {\n LOG.debug(\"log log log \" + expensiveCalculation(object));\n }\n LOG.debug(\"some more logging \" + expensiveCalculation(1));\n }\n\n void good(Object) {\n if (LOG.isDebug()) {\n LOG.debug(\"value: \" + expensiveCalculation(object));\n }\n }\n }' Configure the inspection: Use the Logger class name field to specify the logger class name used. Use the table to specify the logging methods this inspection should warn on, with the corresponding log condition text. Use the Flag all unguarded logging calls option to have the inspection flag all unguarded log calls, not only those with non-constant arguments.", + "markdown": "Reports logging calls with non-constant arguments that are not surrounded by a guard condition. The evaluation of the arguments of a logging call can be expensive. Surrounding a logging call with a guard clause prevents that cost when logging is disabled for the level used by the logging statement. This is especially useful for the least serious level (trace, debug, finest) of logging calls, because those are most often disabled in a production environment.\n\n**Example:**\n\n\n public class Principal {\n void bad(Object object) {\n if (true) {\n LOG.debug(\"log log log \" + expensiveCalculation(object));\n }\n LOG.debug(\"some more logging \" + expensiveCalculation(1));\n }\n\n void good(Object) {\n if (LOG.isDebug()) {\n LOG.debug(\"value: \" + expensiveCalculation(object));\n }\n }\n }\n\n\nConfigure the inspection:\n\n* Use the **Logger class name** field to specify the logger class name used.\n*\n Use the table to specify the logging methods this inspection should warn on, with the corresponding log condition text.\n\n* Use the **Flag all unguarded logging calls** option to have the inspection flag all unguarded log calls, not only those with non-constant arguments." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LogStatementGuardedByLogCondition", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Logging", + "index": 68, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SimplifyOptionalCallChains", + "shortDescription": { + "text": "Optional call chain can be simplified" + }, + "fullDescription": { + "text": "Reports Optional call chains that can be simplified. Here are several examples of possible simplifications: 'optional.map(x -> true).orElse(false)' → 'optional.isPresent()' 'optional.map(x -> Optional.of(x.trim())).orElseGet(Optional::empty)' → 'optional.map(String::trim)' 'optional.map(x -> (String)x).orElse(null)' → '(String) optional.orElse(null)' 'Optional.ofNullable(optional.orElse(null))' → 'optional' 'val = optional.orElse(null); val != null ? val : defaultExpr' → 'optional.orElse(defaultExpr)' 'val = optional.orElse(null); if(val != null) expr(val)' → 'optional.ifPresent(val -> expr(val))' New in 2017.2", + "markdown": "Reports **Optional** call chains that can be simplified. Here are several examples of possible simplifications:\n\n* `optional.map(x -> true).orElse(false)` → `optional.isPresent()`\n* `optional.map(x -> Optional.of(x.trim())).orElseGet(Optional::empty)` → `optional.map(String::trim)`\n* `optional.map(x -> (String)x).orElse(null)` → `(String) optional.orElse(null)`\n* `Optional.ofNullable(optional.orElse(null))` → `optional`\n* `val = optional.orElse(null); val != null ? val : defaultExpr ` → `optional.orElse(defaultExpr)`\n* `val = optional.orElse(null); if(val != null) expr(val) ` → `optional.ifPresent(val -> expr(val))`\n\nNew in 2017.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SimplifyOptionalCallChains", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ModuleWithTooManyClasses", + "shortDescription": { + "text": "Module with too many classes" + }, + "fullDescription": { + "text": "Reports modules that contain too many classes. Overly large modules may indicate a lack of design clarity. Java, Kotlin and Groovy classes are counted. Available only from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name and isn't reported in the editor. Use the Maximum number of classes field to specify the maximum number of classes a module may have.", + "markdown": "Reports modules that contain too many classes. Overly large modules may indicate a lack of design clarity. Java, Kotlin and Groovy classes are counted.\n\nAvailable only from **Code \\| Inspect Code** or\n**Code \\| Analyze Code \\| Run Inspection by Name** and isn't reported in the editor.\n\nUse the **Maximum number of classes** field to specify the maximum number of classes a module may have." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ModuleWithTooManyClasses", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Modularization issues", + "index": 69, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InfiniteLoopStatement", + "shortDescription": { + "text": "Infinite loop statement" + }, + "fullDescription": { + "text": "Reports 'for', 'while', or 'do' statements that can only exit by throwing an exception. While such statements may be correct, they often happen due to coding errors. Example: 'for (;;) {\n }' Use the Ignore when placed in Thread.run option to ignore the infinite loop statements inside 'Thread.run'. It may be useful for the daemon threads. Example: 'new Thread(() -> {\n while (true) {\n }\n }).start();'", + "markdown": "Reports `for`, `while`, or `do` statements that can only exit by throwing an exception. While such statements may be correct, they often happen due to coding errors.\n\nExample:\n\n\n for (;;) {\n }\n\n\nUse the **Ignore when placed in Thread.run** option to ignore the\ninfinite loop statements inside `Thread.run`.\nIt may be useful for the daemon threads.\n\nExample:\n\n\n new Thread(() -> {\n while (true) {\n }\n }).start();\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "InfiniteLoopStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RandomDoubleForRandomInteger", + "shortDescription": { + "text": "Using 'Random.nextDouble()' to get random integer" + }, + "fullDescription": { + "text": "Reports calls to 'java.util.Random.nextDouble()' that are used to create a positive integer number by multiplying the call by a factor and casting to an integer. For generating a random positive integer in a range, 'java.util.Random.nextInt(int)' is simpler and more efficient. Example: 'int getRandomInt() {\n return (int) ((new Random()).nextDouble() * SIZE);\n }'\n After the quick-fix is applied: 'int getRandomInt() {\n return (new Random()).nextInt(SIZE);\n }'", + "markdown": "Reports calls to `java.util.Random.nextDouble()` that are used to create a positive integer number by multiplying the call by a factor and casting to an integer.\n\n\nFor generating a random positive integer in a range,\n`java.util.Random.nextInt(int)` is simpler and more efficient.\n\n**Example:**\n\n\n int getRandomInt() {\n return (int) ((new Random()).nextDouble() * SIZE);\n }\n \nAfter the quick-fix is applied:\n\n\n int getRandomInt() {\n return (new Random()).nextInt(SIZE);\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UsingRandomNextDoubleForRandomInteger", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JavadocHtmlLint", + "shortDescription": { + "text": "HTML problems in Javadoc (DocLint)" + }, + "fullDescription": { + "text": "Reports the same HTML issues in the Javadoc comments that have been reported by DocLint since Java 8. The inspection detects the following issues: Self-closed, unclosed, unknown, misplaced, or empty tag Unknown or wrong attribute Misplaced text Example: '/**\n * Unknown tag: List\n * Unclosed tag: error\n * Misplaced text or tag:

\n * Wrong or empty attribute: \n * Self-closed tag:
\n * ...\n */\nvoid sample(){ }'", + "markdown": "Reports the same HTML issues in the Javadoc comments that have been reported by DocLint since Java 8.\n\nThe inspection detects the following issues:\n\n* Self-closed, unclosed, unknown, misplaced, or empty tag\n* Unknown or wrong attribute\n* Misplaced text\n\nExample:\n\n\n /**\n * Unknown tag: List\n * Unclosed tag: error\n * Misplaced text or tag:
  • one
  • ,
  • two
\n * Wrong or empty attribute: \n * Self-closed tag:
\n * ...\n */\n void sample(){ }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "JavadocHtmlLint", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Javadoc", + "index": 45, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspiciousArrayCast", + "shortDescription": { + "text": "Suspicious array cast" + }, + "fullDescription": { + "text": "Reports suspicious array casts. An array cast is considered suspicious when it casts to a more specific array type. Such a cast is legal at compile time but may fail with a 'ClassCastException' at runtime. Example: 'Number[] numbers = new Number[]{1L, 2L, 4L};\n Long[] longs = (Long[])numbers;'", + "markdown": "Reports suspicious array casts. An array cast is considered suspicious when it casts to a more specific array type. Such a cast is legal at compile time but may fail with a `ClassCastException` at runtime.\n\n**Example:**\n\n\n Number[] numbers = new Number[]{1L, 2L, 4L};\n Long[] longs = (Long[])numbers;\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SuspiciousArrayCast", + "cweIds": [ + 704 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassUnconnectedToPackage", + "shortDescription": { + "text": "Class independent of its package" + }, + "fullDescription": { + "text": "Reports classes that don't depend on any other class in their package and are not a dependency for any other class in their package. Such classes indicate ad-hoc or incoherent packaging strategies and often may be profitably moved. Classes that are the only class in their package are not reported. Available only from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name and isn't reported in the editor.", + "markdown": "Reports classes that don't depend on any other class in their package and are not a dependency for any other class in their package. Such classes indicate ad-hoc or incoherent packaging strategies and often may be profitably moved. Classes that are the only class in their package are not reported.\n\nAvailable only from **Code \\| Inspect Code** or\n**Code \\| Analyze Code \\| Run Inspection by Name** and isn't reported in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassUnconnectedToPackage", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Packaging issues", + "index": 36, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ExceptionNameDoesntEndWithException", + "shortDescription": { + "text": "Exception class name does not end with 'Exception'" + }, + "fullDescription": { + "text": "Reports exception classes whose names don't end with 'Exception'. Example: 'class NotStartedEx extends Exception {}' A quick-fix that renames such classes is available only in the editor.", + "markdown": "Reports exception classes whose names don't end with `Exception`.\n\n**Example:** `class NotStartedEx extends Exception {}`\n\nA quick-fix that renames such classes is available only in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ExceptionClassNameDoesntEndWithException", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions/Class", + "index": 71, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ZeroLengthArrayInitialization", + "shortDescription": { + "text": "Zero-length array allocation" + }, + "fullDescription": { + "text": "Reports allocations of arrays with known lengths of zero. Since array lengths in Java are non-modifiable, it is almost always possible to share zero-length arrays, rather than repeatedly allocate new ones. Such sharing may provide useful optimizations in the program runtime or footprint. Note that the inspection does not report zero-length arrays allocated as static final fields, since those arrays are assumed to be used for implementing array sharing.", + "markdown": "Reports allocations of arrays with known lengths of zero.\n\n\nSince array lengths in Java are non-modifiable, it is almost always possible to share zero-length arrays, rather than repeatedly\nallocate new ones. Such sharing may provide useful optimizations in the program runtime or footprint.\n\n\nNote that the inspection does not report zero-length arrays allocated as static final fields,\nsince those arrays are assumed to be used for implementing array sharing." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ZeroLengthArrayAllocation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Memory", + "index": 73, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonFinalStaticVariableUsedInClassInitialization", + "shortDescription": { + "text": "Non-final static field is used during class initialization" + }, + "fullDescription": { + "text": "Reports the use of non-'final' 'static' variables during class initialization. In such cases, the code semantics may become dependent on the class creation order. Additionally, such cases may lead to the use of variables before their initialization, and generally cause difficult and confusing bugs. Example: 'class Foo {\n public static int bar = 0;\n\n static {\n System.out.println(bar);\n }\n }'", + "markdown": "Reports the use of non-`final` `static` variables during class initialization.\n\nIn such cases, the code semantics may become dependent on the class creation order. Additionally, such cases may lead to the use of\nvariables before their initialization, and generally cause difficult and confusing bugs.\n\n**Example:**\n\n\n class Foo {\n public static int bar = 0;\n\n static {\n System.out.println(bar);\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonFinalStaticVariableUsedInClassInitialization", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Initialization", + "index": 28, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DivideByZero", + "shortDescription": { + "text": "Division by zero" + }, + "fullDescription": { + "text": "Reports division by zero or remainder by zero. Such expressions will produce an 'Infinity', '-Infinity' or 'NaN' result for doubles or floats, and will throw an 'ArithmeticException' for integers. When the expression has a 'NaN' result, the fix suggests replacing the division expression with the 'NaN' constant.", + "markdown": "Reports division by zero or remainder by zero. Such expressions will produce an `Infinity`, `-Infinity` or `NaN` result for doubles or floats, and will throw an `ArithmeticException` for integers.\n\nWhen the expression has a `NaN` result, the fix suggests replacing the division expression with the `NaN` constant." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "divzero", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ThreadStopSuspendResume", + "shortDescription": { + "text": "Call to 'Thread.stop()', 'suspend()' or 'resume()'" + }, + "fullDescription": { + "text": "Reports calls to 'Thread.stop()', 'Thread.suspend()', and 'Thread.resume()'. These calls are inherently prone to data corruption and deadlocks, and their use is strongly discouraged. It is better to use cooperative cancellation instead of 'stop', and interruption instead of direct calls to 'suspend' and 'resume'.", + "markdown": "Reports calls to `Thread.stop()`, `Thread.suspend()`, and `Thread.resume()`.\n\n\nThese calls are inherently prone to data corruption and deadlocks, and their use is strongly discouraged.\nIt is better to use cooperative cancellation instead of `stop`, and\ninterruption instead of direct calls to `suspend` and `resume`." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToThreadStopSuspendOrResumeManager", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryTemporaryOnConversionFromString", + "shortDescription": { + "text": "Unnecessary temporary object in conversion from 'String'" + }, + "fullDescription": { + "text": "Reports unnecessary creation of temporary objects when converting from 'String' to primitive types. Example: 'new Integer(\"3\").intValue()' After the quick-fix is applied: 'Integer.valueOf(\"3\")'", + "markdown": "Reports unnecessary creation of temporary objects when converting from `String` to primitive types.\n\n**Example:**\n\n\n new Integer(\"3\").intValue()\n\nAfter the quick-fix is applied:\n\n\n Integer.valueOf(\"3\")\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryTemporaryOnConversionFromString", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MissingSerialAnnotation", + "shortDescription": { + "text": "'@Serial' annotation could be used" + }, + "fullDescription": { + "text": "Reports methods and fields in the 'Serializable' and 'Externalizable' classes that are suitable to be annotated with the 'java.io.Serial' annotation. The quick-fix adds the annotation. Example: 'class Main implements Serializable {\n private static final long serialVersionUID = 7874493593505141603L;\n\n private void writeObject(ObjectOutputStream out) throws IOException {\n }\n}' After the quick-fix is applied: 'class Main implements Serializable {\n @Serial\n private static final long serialVersionUID = 7874493593505141603L;\n\n @Serial\n private void writeObject(ObjectOutputStream out) throws IOException {\n }\n}' Example: 'class Main implements Externalizable {\n protected Object readResolve() throws ObjectStreamException {\n return \"SomeObject\";\n }\n }' After the quick-fix is applied: 'class Main implements Externalizable {\n @Serial\n protected Object readResolve() throws ObjectStreamException {\n return \"SomeObject\";\n }\n }' For more information about all possible cases, refer the documentation for 'java.io.Serial'. This inspection only reports if the language level of the project or module is 14 or higher. New in 2020.3", + "markdown": "Reports methods and fields in the `Serializable` and `Externalizable` classes that are suitable to be annotated with the `java.io.Serial` annotation. The quick-fix adds the annotation.\n\n**Example:**\n\n\n class Main implements Serializable {\n private static final long serialVersionUID = 7874493593505141603L;\n\n private void writeObject(ObjectOutputStream out) throws IOException {\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Main implements Serializable {\n @Serial\n private static final long serialVersionUID = 7874493593505141603L;\n\n @Serial\n private void writeObject(ObjectOutputStream out) throws IOException {\n }\n }\n\n**Example:**\n\n\n class Main implements Externalizable {\n protected Object readResolve() throws ObjectStreamException {\n return \"SomeObject\";\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Main implements Externalizable {\n @Serial\n protected Object readResolve() throws ObjectStreamException {\n return \"SomeObject\";\n }\n }\n\nFor more information about all possible cases, refer the documentation for `java.io.Serial`.\n\nThis inspection only reports if the language level of the project or module is 14 or higher.\n\nNew in 2020.3" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "MissingSerialAnnotation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MultipleExceptionsDeclaredOnTestMethod", + "shortDescription": { + "text": "Multiple exceptions declared on test method" + }, + "fullDescription": { + "text": "Reports JUnit test method 'throws' clauses with more than one exception. Such clauses are unnecessarily verbose. Test methods will not be called from other project code, so there is no need to handle these exceptions separately. For example: '@Test\n public void testReflection() throws NoSuchMethodException,\n InvocationTargetException, IllegalAccessException {\n String result = (String) String.class.getMethod(\"trim\")\n .invoke(\" hello \");\n assertEquals(\"hello\", result);\n }' A quick fix is provided to replace the exception declarations with a single exception: '@Test\n public void testReflection() throws Exception {\n String result = (String) String.class.getMethod(\"trim\")\n .invoke(\" hello \");\n assertEquals(\"hello\", result);\n }'", + "markdown": "Reports JUnit test method `throws` clauses with more than one exception. Such clauses are unnecessarily verbose. Test methods will not be called from other project code, so there is no need to handle these exceptions separately.\n\nFor example:\n\n\n @Test\n public void testReflection() throws NoSuchMethodException,\n InvocationTargetException, IllegalAccessException {\n String result = (String) String.class.getMethod(\"trim\")\n .invoke(\" hello \");\n assertEquals(\"hello\", result);\n }\n\nA quick fix is provided to replace the exception declarations with a single exception:\n\n\n @Test\n public void testReflection() throws Exception {\n String result = (String) String.class.getMethod(\"trim\")\n .invoke(\" hello \");\n assertEquals(\"hello\", result);\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MultipleExceptionsDeclaredOnTestMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/JUnit", + "index": 77, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringConcatenationMissingWhitespace", + "shortDescription": { + "text": "Whitespace may be missing in string concatenation" + }, + "fullDescription": { + "text": "Reports string concatenations with missing whitespaces, that is where the left-hand side ends with a Unicode letter or digit and the right-hand side starts with a Unicode letter or digit. Example: 'String sql = \"SELECT column\" +\n \"FROM table\";' Use the Ignore concatenations with variable strings option to only report when both the left and right side of the concatenation are literals.", + "markdown": "Reports string concatenations with missing whitespaces, that is where the left-hand side ends with a Unicode letter or digit and the right-hand side starts with a Unicode letter or digit.\n\n**Example:**\n\n\n String sql = \"SELECT column\" +\n \"FROM table\";\n\n\nUse the **Ignore concatenations with variable strings** option to only report\nwhen both the left and right side of the concatenation are literals." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StringConcatenationMissingWhitespace", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StandardVariableNames", + "shortDescription": { + "text": "Standard variable names" + }, + "fullDescription": { + "text": "Reports variables with 'standard' names that do not correspond to their types. Such names may be confusing. There are the following standard names for specific types: i, j, k, m, n - 'int' f - 'float' d - 'double' b - 'byte' c, ch - 'char' l - 'long' s, str - 'String' Rename quick-fix is suggested only in the editor. Use the option to ignore parameter names which are identical to the parameter name from a direct super method.", + "markdown": "Reports variables with 'standard' names that do not correspond to their types. Such names may be confusing. There are the following standard names for specific types:\n\n* i, j, k, m, n - `int`\n* f - `float`\n* d - `double`\n* b - `byte`\n* c, ch - `char`\n* l - `long`\n* s, str - `String`\n\nRename quick-fix is suggested only in the editor.\n\n\nUse the option to ignore parameter names which are identical to the parameter name from a direct super method." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StandardVariableNames", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions", + "index": 51, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UseOfConcreteClass", + "shortDescription": { + "text": "Use of concrete class" + }, + "fullDescription": { + "text": "Reports usages of concrete classes, rather than interfaces. Such declarations may represent a failure of abstraction and may make testing more difficult. Declarations whose classes come from system or third-party libraries will not be reported by this inspection. Casts, instanceofs, and local variables are not reported in 'equals()' method implementations. Also, casts are not reported in 'clone()' method implementations. Example: 'interface Entity {}\n class EntityImpl implements Entity {}\n\n void processObject(Object obj) {\n // warning: instanceof of the concrete class\n if (obj instanceof EntityImpl) {\n // warning: cast to the concrete class,\n // rather than the interface\n processEntity((EntityImpl)obj);\n }\n }\n // warning: parameter of concrete class\n void processEntity(EntityImpl obj) {\n }' Use the Ignore abstract class type option to ignore casts to abstract classes. Use the subsequent options to control contexts where the problem is reported.", + "markdown": "Reports usages of concrete classes, rather than interfaces. Such declarations may represent a failure of abstraction and may make testing more difficult.\n\n\nDeclarations whose classes come from system or third-party libraries will not be reported by this inspection.\nCasts, instanceofs, and local variables are not reported in `equals()` method implementations.\nAlso, casts are not reported in `clone()` method implementations.\n\nExample:\n\n\n interface Entity {}\n class EntityImpl implements Entity {}\n\n void processObject(Object obj) {\n // warning: instanceof of the concrete class\n if (obj instanceof EntityImpl) {\n // warning: cast to the concrete class,\n // rather than the interface\n processEntity((EntityImpl)obj);\n }\n }\n // warning: parameter of concrete class\n void processEntity(EntityImpl obj) {\n }\n\n\nUse the **Ignore abstract class type** option to ignore casts to abstract classes.\n\nUse the subsequent options to control contexts where the problem is reported." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UseOfConcreteClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Abstraction issues", + "index": 78, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DeconstructionCanBeUsed", + "shortDescription": { + "text": "Record pattern can be used" + }, + "fullDescription": { + "text": "Reports patterns that can be replaced with record patterns. Example: 'record Point(int x, int y) {}\n\n static void printSum(Object obj) {\n if (obj instanceof Point p) {\n int x = p.x();\n int y = p.y();\n System.out.println(x + y);\n }\n }' After the quick-fix is applied: 'record Point(int x, int y) {}\n\n static void printSum(Object obj) {\n if (obj instanceof Point(int x, int y)) {\n System.out.println(x + y);\n }\n }' This inspection only reports if the language level of the project or module is 19 or higher New in 2023.1", + "markdown": "Reports patterns that can be replaced with record patterns.\n\nExample:\n\n\n record Point(int x, int y) {}\n\n static void printSum(Object obj) {\n if (obj instanceof Point p) {\n int x = p.x();\n int y = p.y();\n System.out.println(x + y);\n }\n }\n\nAfter the quick-fix is applied:\n\n\n record Point(int x, int y) {}\n\n static void printSum(Object obj) {\n if (obj instanceof Point(int x, int y)) {\n System.out.println(x + y);\n }\n }\n\nThis inspection only reports if the language level of the project or module is 19 or higher\n\nNew in 2023.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "DeconstructionCanBeUsed", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 19", + "index": 79, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantLabeledSwitchRuleCodeBlock", + "shortDescription": { + "text": "Labeled switch rule has redundant code block" + }, + "fullDescription": { + "text": "Reports labeled rules of 'switch' statements or 'switch' expressions that have a redundant code block. Example: 'String s = switch (n) {\n case 1 -> { yield Integer.toString(n); }\n default -> \"default\";\n };' After the quick-fix is applied: 'String s = switch (n) {\n case 1 -> Integer.toString(n);\n default -> \"default\";\n };' This inspection only reports if the language level of the project or module is 14 or higher. New in 2019.1", + "markdown": "Reports labeled rules of `switch` statements or `switch` expressions that have a redundant code block.\n\nExample:\n\n\n String s = switch (n) {\n case 1 -> { yield Integer.toString(n); }\n default -> \"default\";\n };\n\nAfter the quick-fix is applied:\n\n\n String s = switch (n) {\n case 1 -> Integer.toString(n);\n default -> \"default\";\n };\n\nThis inspection only reports if the language level of the project or module is 14 or higher.\n\nNew in 2019.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantLabeledSwitchRuleCodeBlock", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IOStreamConstructor", + "shortDescription": { + "text": "'InputStream' and 'OutputStream' can be constructed using 'Files' methods" + }, + "fullDescription": { + "text": "Reports 'new FileInputStream()' or 'new FileOutputStream()' expressions that can be replaced with 'Files.newInputStream()' or 'Files.newOutputStream()' calls respectively. The streams created using 'Files' methods are usually more efficient than those created by stream constructors. Example: 'InputStream is = new BufferedInputStream(new FileInputStream(file));' After the quick-fix is applied: 'InputStream is = new BufferedInputStream(Files.newInputStream(file.toPath()));' This inspection does not show warning if the language level 10 or higher, but the quick-fix is still available. This inspection only reports if the language level of the project or module is 7 or higher. New in 2022.1", + "markdown": "Reports `new FileInputStream()` or `new FileOutputStream()` expressions that can be replaced with `Files.newInputStream()` or `Files.newOutputStream()` calls respectively. \nThe streams created using `Files` methods are usually more efficient than those created by stream constructors.\n\nExample:\n\n\n InputStream is = new BufferedInputStream(new FileInputStream(file));\n\nAfter the quick-fix is applied:\n\n\n InputStream is = new BufferedInputStream(Files.newInputStream(file.toPath()));\n\nThis inspection does not show warning if the language level 10 or higher, but the quick-fix is still available.\n\nThis inspection only reports if the language level of the project or module is 7 or higher.\n\nNew in 2022.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "IOStreamConstructor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AssignmentToForLoopParameter", + "shortDescription": { + "text": "Assignment to 'for' loop parameter" + }, + "fullDescription": { + "text": "Reports assignment to, or modification of a 'for' loop parameter inside the body of the loop. Although occasionally intended, this construct may be confusing and is often the result of a typo or a wrong variable being used. The quick-fix adds a declaration of a new variable. Example: 'for (String s : list) {\n // Warning: s is changed inside the loop\n s = s.trim();\n System.out.println(\"String: \" + s);\n }' After the quick-fix is applied: 'for (String s : list) {\n String trimmed = s.trim();\n System.out.println(\"String: \" + trimmed);\n }' Assignments in basic 'for' loops without an update statement are not reported. In such cases the assignment is probably intended and can't be easily moved to the update part of the 'for' loop. Example: 'for (int i = 0; i < list.size(); ) {\n if (element.equals(list.get(i))) {\n list.remove(i);\n } else {\n // modification of for loop parameter is not reported\n // as there's no update statement\n i++;\n }\n }' Use the Check enhanced 'for' loop parameters option to specify whether modifications of enhanced 'for' loop parameters should be also reported.", + "markdown": "Reports assignment to, or modification of a `for` loop parameter inside the body of the loop.\n\nAlthough occasionally intended, this construct may be confusing and is often the result of a typo or a wrong variable being used.\n\nThe quick-fix adds a declaration of a new variable.\n\n**Example:**\n\n\n for (String s : list) {\n // Warning: s is changed inside the loop\n s = s.trim();\n System.out.println(\"String: \" + s);\n }\n\nAfter the quick-fix is applied:\n\n\n for (String s : list) {\n String trimmed = s.trim();\n System.out.println(\"String: \" + trimmed);\n }\n\nAssignments in basic `for` loops without an update statement are not reported.\nIn such cases the assignment is probably intended and can't be easily moved to the update part of the `for` loop.\n\n**Example:**\n\n\n for (int i = 0; i < list.size(); ) {\n if (element.equals(list.get(i))) {\n list.remove(i);\n } else {\n // modification of for loop parameter is not reported\n // as there's no update statement\n i++;\n }\n }\n\nUse the **Check enhanced 'for' loop parameters** option to specify whether modifications of enhanced `for` loop parameters\nshould be also reported." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AssignmentToForLoopParameter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Assignment issues", + "index": 34, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java9CollectionFactory", + "shortDescription": { + "text": "Immutable collection creation can be replaced with collection factory call" + }, + "fullDescription": { + "text": "Reports 'java.util.Collections' unmodifiable collection calls that can be converted to newer collection factory methods. These can be replaced with e.g. 'List.of()' or 'Set.of()' introduced in Java 9 or 'List.copyOf()' introduced in Java 10. Note that in contrast to 'java.util.Collections' methods, Java 9 collection factory methods: Do not accept 'null' values. Require unique set elements and map keys. Do not accept 'null' arguments to query methods like 'List.contains()' or 'Map.get()' of the collections returned. When these cases are violated, exceptions are thrown. This can change the semantics of the code after the migration. Example: 'List even = Collections.unmodifiableList(\n Arrays.asList(2, 4, 6, 8, 10, 2));\n List evenCopy = Collections.unmodifiableList(\n new ArrayList<>(list1));' After the quick-fix is applied: 'List even = List.of(2, 4, 6, 8, 10, 2);\n List evenCopy = List.copyOf(list);' This inspection only reports if the language level of the project or module is 9 or higher. Use the Do not warn when content is non-constant option to report only in cases when the supplied arguments are compile-time constants. This reduces the chances that the behavior changes, because it's not always possible to statically check whether original elements are unique and not 'null'. Use the Suggest 'Map.ofEntries' option to suggest replacing unmodifiable maps with more than 10 entries with 'Map.ofEntries()'. New in 2017.2", + "markdown": "Reports `java.util.Collections` unmodifiable collection calls that can be converted to newer collection factory methods. These can be replaced with e.g. `List.of()` or `Set.of()` introduced in Java 9 or `List.copyOf()` introduced in Java 10.\n\nNote that in contrast to `java.util.Collections` methods, Java 9 collection factory methods:\n\n* Do not accept `null` values.\n* Require unique set elements and map keys.\n* Do not accept `null` arguments to query methods like `List.contains()` or `Map.get()` of the collections returned.\n\nWhen these cases are violated, exceptions are thrown.\nThis can change the semantics of the code after the migration.\n\nExample:\n\n\n List even = Collections.unmodifiableList(\n Arrays.asList(2, 4, 6, 8, 10, 2));\n List evenCopy = Collections.unmodifiableList(\n new ArrayList<>(list1));\n\nAfter the quick-fix is applied:\n\n\n List even = List.of(2, 4, 6, 8, 10, 2);\n List evenCopy = List.copyOf(list);\n\nThis inspection only reports if the language level of the project or module is 9 or higher.\n\n\nUse the **Do not warn when content is non-constant** option to report only in cases when the supplied arguments are compile-time constants.\nThis reduces the chances that the behavior changes,\nbecause it's not always possible to statically check whether original elements are unique and not `null`.\n\n\nUse the **Suggest 'Map.ofEntries'** option to suggest replacing unmodifiable maps with more than 10 entries with `Map.ofEntries()`.\n\nNew in 2017.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "Java9CollectionFactory", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 9", + "index": 80, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ExceptionFromCatchWhichDoesntWrap", + "shortDescription": { + "text": "'throw' inside 'catch' block which ignores the caught exception" + }, + "fullDescription": { + "text": "Reports exceptions that are thrown from inside 'catch' blocks but do not \"wrap\" the caught exception. When an exception is thrown in response to an exception, wrapping the initial exception prevents losing valuable context information, such as stack frames and line numbers. Example: '...\n catch (IOException e) {\n closeAllConnections();\n throw new ConnectException(\"Connection problem.\"); // warning: 'throw' inside 'catch' block ignores the caught exception 'e'\n }' Configure the inspection: Use the Ignore if result of exception method call is used option to indicate whether the inspection should ignore exceptions whose argument is the result of a method call on the original exception, such as 'getMessage()'. Use the Ignore if thrown exception cannot wrap an exception option to ignore 'throw' statements that throw exceptions without a constructor that accepts a 'Throwable' cause.", + "markdown": "Reports exceptions that are thrown from inside `catch` blocks but do not \"wrap\" the caught exception.\n\nWhen an exception is thrown in response to an exception, wrapping the initial exception prevents losing valuable context information,\nsuch as stack frames and line numbers.\n\n**Example:**\n\n\n ...\n catch (IOException e) {\n closeAllConnections();\n throw new ConnectException(\"Connection problem.\"); // warning: 'throw' inside 'catch' block ignores the caught exception 'e'\n }\n\nConfigure the inspection:\n\n* Use the **Ignore if result of exception method call is used** option to indicate whether the inspection should ignore exceptions whose argument is the result of a method call on the original exception, such as `getMessage()`.\n* Use the **Ignore if thrown exception cannot wrap an exception** option to ignore `throw` statements that throw exceptions without a constructor that accepts a `Throwable` cause." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ThrowInsideCatchBlockWhichIgnoresCaughtException", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MethodOnlyUsedFromInnerClass", + "shortDescription": { + "text": "Private method only used from inner class" + }, + "fullDescription": { + "text": "Reports 'private' methods which are only called from an inner class of the class containing the method. Such methods can be safely moved into that inner class. Example: 'public class Outer {\n public static void main(String[] args) {\n new Inner().run(args[0]);\n }\n\n static class Inner {\n void run(String arg) {\n // Method isEmpty() is used from Inner class only\n // consider moving it to the Inner class\n if (!isEmpty(arg)) {\n System.out.println(\"Argument is supplied\");\n }\n }\n }\n\n private static boolean isEmpty(String s) {\n return s != null && s.trim().isEmpty();\n }\n}' Use the first checkbox below to ignore 'private' methods which are called from an anonymous or local class. Use the third checkbox to only report 'static' methods.", + "markdown": "Reports `private` methods which are only called from an inner class of the class containing the method. Such methods can be safely moved into that inner class.\n\nExample:\n\n\n public class Outer {\n public static void main(String[] args) {\n new Inner().run(args[0]);\n }\n\n static class Inner {\n void run(String arg) {\n // Method isEmpty() is used from Inner class only\n // consider moving it to the Inner class\n if (!isEmpty(arg)) {\n System.out.println(\"Argument is supplied\");\n }\n }\n }\n\n private static boolean isEmpty(String s) {\n return s != null && s.trim().isEmpty();\n }\n }\n\n\nUse the first checkbox below to ignore `private`\nmethods which are called from an anonymous or local class.\n\n\nUse the third checkbox to only report `static` methods." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MethodOnlyUsedFromInnerClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Abstraction issues", + "index": 78, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ComparisonToNaN", + "shortDescription": { + "text": "Comparison to 'Double.NaN' or 'Float.NaN'" + }, + "fullDescription": { + "text": "Reports any comparisons to 'Double.NaN' or 'Float.NaN'. Such comparisons are never meaningful, as NaN is not equal to anything, including itself. Use the 'Double.isNaN()' or 'Float.isNaN()' methods instead. Example: 'if (x == Double.NaN) {...}' After the quick-fix is applied: 'if (Double.isNaN(x)) {...}'", + "markdown": "Reports any comparisons to `Double.NaN` or `Float.NaN`. Such comparisons are never meaningful, as NaN is not equal to anything, including itself. Use the `Double.isNaN()` or `Float.isNaN()` methods instead.\n\n**Example:**\n\n\n if (x == Double.NaN) {...}\n\nAfter the quick-fix is applied:\n\n\n if (Double.isNaN(x)) {...}\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ComparisonToNaN", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MultiCatchCanBeSplit", + "shortDescription": { + "text": "Multi-catch can be split into separate catch blocks" + }, + "fullDescription": { + "text": "Reports multi-'catch' sections and suggests splitting them into separate 'catch' blocks. Example: 'try {\n int i = getIndex();\n } catch (NullPointerException|IndexOutOfBoundsException e) {\n e.printStackTrace();\n }' After the quick-fix is applied: 'try {\n int i = getIndex();\n } catch (NullPointerException e) {\n e.printStackTrace();\n } catch (IndexOutOfBoundsException e) {\n e.printStackTrace();\n }' Multi-'catch' appeared in Java 7. This inspection can help to downgrade for backward compatibility with earlier Java versions.", + "markdown": "Reports multi-`catch` sections and suggests splitting them into separate `catch` blocks.\n\nExample:\n\n\n try {\n int i = getIndex();\n } catch (NullPointerException|IndexOutOfBoundsException e) {\n e.printStackTrace();\n }\n\nAfter the quick-fix is applied:\n\n\n try {\n int i = getIndex();\n } catch (NullPointerException e) {\n e.printStackTrace();\n } catch (IndexOutOfBoundsException e) {\n e.printStackTrace();\n }\n\n\n*Multi-* `catch` appeared in Java 7.\nThis inspection can help to downgrade for backward compatibility with earlier Java versions." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "MultiCatchCanBeSplit", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ResultSetIndexZero", + "shortDescription": { + "text": "Use of index 0 in JDBC ResultSet" + }, + "fullDescription": { + "text": "Reports attempts to access column 0 of 'java.sql.ResultSet' or 'java.sql.PreparedStatement'. For historical reasons, columns of 'java.sql.ResultSet' and 'java.sql.PreparedStatement' are numbered starting with 1, rather than with 0, and accessing column 0 is a common error in JDBC programming. Example: 'String getName(ResultSet rs) {\n return rs.getString(0);\n }'", + "markdown": "Reports attempts to access column 0 of `java.sql.ResultSet` or `java.sql.PreparedStatement`. For historical reasons, columns of `java.sql.ResultSet` and `java.sql.PreparedStatement` are numbered starting with **1** , rather than with **0** , and accessing column 0 is a common error in JDBC programming.\n\n**Example:**\n\n\n String getName(ResultSet rs) {\n return rs.getString(0);\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UseOfIndexZeroInJDBCResultSet", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConditionCoveredByFurtherCondition", + "shortDescription": { + "text": "Condition is covered by further condition" + }, + "fullDescription": { + "text": "Reports conditions that become redundant as they are completely covered by a subsequent condition. For example, in the 'value != -1 && value > 0' condition, the first part is redundant: if it's false, then the second part is also false. Or in a condition like 'obj != null && obj instanceof String', the null-check is redundant as 'instanceof' operator implies non-nullity. New in 2018.3", + "markdown": "Reports conditions that become redundant as they are completely covered by a subsequent condition.\n\nFor example, in the `value != -1 && value > 0` condition, the first part is redundant:\nif it's false, then the second part is also false.\nOr in a condition like `obj != null && obj instanceof String`,\nthe null-check is redundant as `instanceof` operator implies non-nullity.\n\nNew in 2018.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ConditionCoveredByFurtherCondition", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MetaAnnotationWithoutRuntimeRetention", + "shortDescription": { + "text": "Test annotation without '@Retention(RUNTIME)' annotation" + }, + "fullDescription": { + "text": "Reports annotations with a 'SOURCE' or 'CLASS' retention policy that are supposed to be used by JUnit 5. Such annotations are not available at runtime and most probably their retention policy should be fixed to be accessible through reflection. Note that if the retention policy is not specified, then the default retention policy 'CLASS' is used. Example: '@Testable\n public @interface UnitTest {}' After the quick-fix is applied: '@Retention(RetentionPolicy.RUNTIME)\n @Testable\n public @interface UnitTest {}'", + "markdown": "Reports annotations with a `SOURCE` or `CLASS` retention policy that are supposed to be used by JUnit 5. Such annotations are not available at runtime and most probably their retention policy should be fixed to be accessible through reflection.\n\nNote that if the retention policy is not specified, then the default retention policy `CLASS` is used.\n\n**Example:**\n\n\n @Testable\n public @interface UnitTest {}\n\nAfter the quick-fix is applied:\n\n\n @Retention(RetentionPolicy.RUNTIME)\n @Testable\n public @interface UnitTest {}\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "MetaAnnotationWithoutRuntimeRetention", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/JUnit", + "index": 77, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PatternVariableHidesField", + "shortDescription": { + "text": "Pattern variable hides field" + }, + "fullDescription": { + "text": "Reports pattern variables named identically to a field of a surrounding class. As a result of such naming, you may accidentally use the pattern variable when using the identically named field is intended. A quick-fix is suggested to rename the variable. Example: 'class Pointless {\n Point p = new Point();\n\n public void test(Object a) {\n if (a instanceof Point p) {\n System.out.print(\"a is a point (\" + p.x + \", \" + p.y + ')');\n } else {\n System.out.print(\"p is a point (\" + p.x + \", \" + p.y + ')');\n }\n }\n }' New in 2022.2", + "markdown": "Reports pattern variables named identically to a field of a surrounding class. As a result of such naming, you may accidentally use the pattern variable when using the identically named field is intended.\n\n\nA quick-fix is suggested to rename the variable.\n\n**Example:**\n\n\n class Pointless {\n Point p = new Point();\n\n public void test(Object a) {\n if (a instanceof Point p) {\n System.out.print(\"a is a point (\" + p.x + \", \" + p.y + ')');\n } else {\n System.out.print(\"p is a point (\" + p.x + \", \" + p.y + ')');\n }\n }\n }\n\nNew in 2022.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "PatternVariableHidesField", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Visibility", + "index": 57, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JoinDeclarationAndAssignmentJava", + "shortDescription": { + "text": "Assignment can be joined with declaration" + }, + "fullDescription": { + "text": "Reports variable assignments that can be joined with a variable declaration. Example: 'int x;\n x = 1;' The quick-fix converts the assignment into an initializer: 'int x = 1;' New in 2018.3", + "markdown": "Reports variable assignments that can be joined with a variable declaration.\n\nExample:\n\n\n int x;\n x = 1;\n\nThe quick-fix converts the assignment into an initializer:\n\n\n int x = 1;\n\nNew in 2018.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "JoinDeclarationAndAssignmentJava", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryContinue", + "shortDescription": { + "text": "Unnecessary 'continue' statement" + }, + "fullDescription": { + "text": "Reports 'continue' statements if they are the last reachable statements in the loop. These 'continue' statements are unnecessary and can be safely removed. Example: 'for (String element: elements) {\n System.out.println();\n continue;\n }' After the quick-fix is applied: 'for (String element: elements) {\n System.out.println();\n }' The inspection doesn't analyze JSP files. Use the Ignore in then branch of 'if' statement with 'else' branch option to ignore 'continue' statements when they are placed in a 'then' branch of a complete 'if'-'else' statement. Example: 'for (String element: elements) {\n if(element.isEmpty()) {\n continue;\n } else {\n //...\n }\n }'", + "markdown": "Reports `continue` statements if they are the last reachable statements in the loop. These `continue` statements are unnecessary and can be safely removed.\n\nExample:\n\n\n for (String element: elements) {\n System.out.println();\n continue;\n }\n\nAfter the quick-fix is applied:\n\n\n for (String element: elements) {\n System.out.println();\n }\n\nThe inspection doesn't analyze JSP files.\n\n\nUse the **Ignore in then branch of 'if' statement with 'else' branch** option to ignore\n`continue` statements when they are placed in a `then` branch of a complete\n`if`-`else` statement.\n\nExample:\n\n\n for (String element: elements) {\n if(element.isEmpty()) {\n continue;\n } else {\n //...\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryContinue", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InnerClassVariableHidesOuterClassVariable", + "shortDescription": { + "text": "Inner class field hides outer class field" + }, + "fullDescription": { + "text": "Reports inner class fields named identically to a field of a surrounding class. As a result of such naming, you may accidentally use the field from the inner class when using the identically named field of a surrounding class is intended. A quick-fix is suggested to rename the inner class field. Example: 'class Outer {\n private String name;\n\n class Inner {\n private String name;\n }\n }' Use the option to choose whether this inspection should report all name clashes, or only clashes with fields that are visible from the inner class.", + "markdown": "Reports inner class fields named identically to a field of a surrounding class. As a result of such naming, you may accidentally use the field from the inner class when using the identically named field of a surrounding class is intended.\n\nA quick-fix is suggested to rename the inner class field.\n\n**Example:**\n\n\n class Outer {\n private String name;\n\n class Inner {\n private String name;\n }\n }\n\n\nUse the option to choose whether this inspection should report all name clashes,\nor only clashes with fields that are visible from the inner class." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "InnerClassFieldHidesOuterClassField", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Visibility", + "index": 57, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonThreadSafeLazyInitialization", + "shortDescription": { + "text": "Unsafe lazy initialization of 'static' field" + }, + "fullDescription": { + "text": "Reports 'static' variables that are lazily initialized in a non-thread-safe manner. Lazy initialization of 'static' variables should be done with an appropriate synchronization construct to prevent different threads from performing conflicting initialization. When applicable, a quick-fix, which introduces the lazy initialization holder class idiom, is suggested. This idiom makes use of the fact that the JVM guarantees that a class will not be initialized until it is used. Example: 'class X {\n private static List list;\n\n public List getList() {\n if (list == null) {\n list = List.of(\"one\", \"two\", \"tree\");\n }\n return list;\n }\n }' After the quick-fix is applied: 'class X {\n private static final class ListHolder {\n static final List list = List.of(\"one\", \"two\", \"tree\");\n }\n\n public List getList() {\n return ListHolder.list;\n }\n }'", + "markdown": "Reports `static` variables that are lazily initialized in a non-thread-safe manner.\n\nLazy initialization of `static` variables should be done with an appropriate synchronization construct\nto prevent different threads from performing conflicting initialization.\n\nWhen applicable, a quick-fix, which introduces the\n[lazy initialization holder class idiom](https://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom), is suggested.\nThis idiom makes use of the fact that the JVM guarantees that a class will not be initialized until it is used.\n\n**Example:**\n\n\n class X {\n private static List list;\n\n public List getList() {\n if (list == null) {\n list = List.of(\"one\", \"two\", \"tree\");\n }\n return list;\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class X {\n private static final class ListHolder {\n static final List list = List.of(\"one\", \"two\", \"tree\");\n }\n\n public List getList() {\n return ListHolder.list;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonThreadSafeLazyInitialization", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Initialization", + "index": 28, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryModifier", + "shortDescription": { + "text": "Unnecessary modifier" + }, + "fullDescription": { + "text": "Reports redundant modifiers and suggests to remove them. The resulting code will be shorter, but the behaviour and meaning will remain the same. Example 1: '// all code is implicitly strictfp under Java 17 and higher\n strictfp class X {\n\n // inner enums are implicitly static\n static enum Inner {\n A, B, C\n }\n\n // inner records are implicitly static\n static record R() {\n }\n }' Example 2: 'final record R() {\n // all records are implicitly final\n }' Example 3: '// all interfaces are implicitly abstract\n abstract interface Printer {\n\n // all interface members are implicitly public\n public int size();\n\n // all inner classes of interfaces are implicitly static\n static class Inner {}\n }'", + "markdown": "Reports redundant modifiers and suggests to remove them. The resulting code will be shorter, but the behaviour and meaning will remain the same.\n\n**Example 1:**\n\n\n // all code is implicitly strictfp under Java 17 and higher\n strictfp class X {\n\n // inner enums are implicitly static\n static enum Inner {\n A, B, C\n }\n\n // inner records are implicitly static\n static record R() {\n }\n }\n\n**Example 2:**\n\n\n final record R() {\n // all records are implicitly final\n }\n\n**Example 3:**\n\n\n // all interfaces are implicitly abstract\n abstract interface Printer {\n\n // all interface members are implicitly public\n public int size();\n\n // all inner classes of interfaces are implicitly static\n static class Inner {}\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryModifier", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConditionalCanBePushedInsideExpression", + "shortDescription": { + "text": "Conditional can be pushed inside branch expression" + }, + "fullDescription": { + "text": "Reports conditional expressions with 'then' and else branches that are similar enough so that the expression can be moved inside. This action shortens the code. Example: 'double g(int a, int b) {\n return a == b ? Math.cos(0) : Math.cos(1);\n }' After the quick-fix is applied: 'double g(int a, int b) {\n return Math.cos(a == b ? 0 : 1);\n }' New in 2017.2", + "markdown": "Reports conditional expressions with `then` and else branches that are similar enough so that the expression can be moved inside. This action shortens the code.\n\nExample:\n\n\n double g(int a, int b) {\n return a == b ? Math.cos(0) : Math.cos(1);\n }\n\nAfter the quick-fix is applied:\n\n\n double g(int a, int b) {\n return Math.cos(a == b ? 0 : 1);\n }\n\nNew in 2017.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ConditionalCanBePushedInsideExpression", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CloneInNonCloneableClass", + "shortDescription": { + "text": "'clone()' method in non-Cloneable class" + }, + "fullDescription": { + "text": "Reports classes that override the 'clone()' method but don't implement the 'Cloneable' interface. This usually represents a programming error. Use the Only warn on 'public' clone methods option to ignore methods that aren't 'public'. For classes designed to be inherited, you may choose to override 'clone()' and declare it as 'protected' without implementing the 'Cloneable' interface and decide whether to implement the 'Cloneable' interface in subclasses.", + "markdown": "Reports classes that override the `clone()` method but don't implement the `Cloneable` interface. This usually represents a programming error.\n\n\nUse the **Only warn on 'public' clone methods** option to ignore methods that aren't `public`.\n\nFor classes designed to be inherited, you may choose to override `clone()` and declare it as `protected`\nwithout implementing the `Cloneable` interface and decide whether to implement the `Cloneable` interface in subclasses." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CloneInNonCloneableClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Cloning issues", + "index": 82, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CStyleArrayDeclaration", + "shortDescription": { + "text": "C-style array declaration" + }, + "fullDescription": { + "text": "Reports array declarations written in C-style syntax, where the array brackets are placed after a variable name or after a method parameter list. Most code styles prefer Java-style array declarations, where the array brackets are placed after the type name. Example: 'public String process(String value[])[] {\n return value;\n }' After the quick-fix is applied: 'public String[] process(String[] value) {\n return value;\n }' Configure the inspection: Use the Ignore C-style declarations in variables option to report C-style array declaration of method return types only.", + "markdown": "Reports array declarations written in C-style syntax, where the array brackets are placed after a variable name or after a method parameter list. Most code styles prefer Java-style array declarations, where the array brackets are placed after the type name.\n\n**Example:**\n\n\n public String process(String value[])[] {\n return value;\n }\n\nAfter the quick-fix is applied:\n\n\n public String[] process(String[] value) {\n return value;\n }\n\nConfigure the inspection:\n\n\nUse the **Ignore C-style declarations in variables** option to report C-style array declaration of method return types only." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CStyleArrayDeclaration", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java8ListReplaceAll", + "shortDescription": { + "text": "Loop can be replaced with 'List.replaceAll()'" + }, + "fullDescription": { + "text": "Reports loops which can be collapsed into a single 'List.replaceAll()' call. Example: 'for (int i = 0; i < strings.size(); i++) {\n String str = strings.get(i).toLowerCase();\n strings.set(i, str);\n }' After the quick-fix is applied: 'strings.replaceAll(String::toLowerCase);' This inspection only reports if the language level of the project or module is 8 or higher. New in 2022.1", + "markdown": "Reports loops which can be collapsed into a single `List.replaceAll()` call.\n\n**Example:**\n\n\n for (int i = 0; i < strings.size(); i++) {\n String str = strings.get(i).toLowerCase();\n strings.set(i, str);\n }\n\nAfter the quick-fix is applied:\n\n\n strings.replaceAll(String::toLowerCase);\n\n\nThis inspection only reports if the language level of the project or module is 8 or higher.\n\nNew in 2022.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "Java8ListReplaceAll", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 8", + "index": 62, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SystemExit", + "shortDescription": { + "text": "Call to 'System.exit()' or related methods" + }, + "fullDescription": { + "text": "Reports calls to 'System.exit()', 'Runtime.exit()', and 'Runtime.halt()'. Invoking 'System.exit()' or 'Runtime.exit()' calls the shutdown hooks and terminates the currently running Java virtual machine. Invoking 'Runtime.halt()' forcibly terminates the JVM without causing shutdown hooks to be started. Each of these methods should be used with extreme caution. Calls to these methods make the calling code unportable to most application servers. Use the option to ignore calls in main methods.", + "markdown": "Reports calls to `System.exit()`, `Runtime.exit()`, and `Runtime.halt()`.\n\n\nInvoking `System.exit()` or `Runtime.exit()`\ncalls the shutdown hooks and terminates the currently running Java\nvirtual machine. Invoking `Runtime.halt()` forcibly\nterminates the JVM without causing shutdown hooks to be started.\nEach of these methods should be used with extreme caution. Calls\nto these methods make the calling code unportable to most\napplication servers.\n\n\nUse the option to ignore calls in main methods." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToSystemExit", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Portability", + "index": 7, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BigDecimalLegacyMethod", + "shortDescription": { + "text": "'BigDecimal' legacy method called" + }, + "fullDescription": { + "text": "Reports calls to 'BigDecimal.divide()' or 'BigDecimal.setScale()' that use integer constants to specify the rounding mode. Since JDK 1.5, consider using methods that take the 'RoundingMode' 'enum' parameter instead. Example: 'new BigDecimal(\"42\").setScale(2, BigDecimal.ROUND_FLOOR);' After the quick-fix is applied: 'new BigDecimal(\"42\").setScale(2, RoundingMode.FLOOR);'", + "markdown": "Reports calls to `BigDecimal.divide()` or `BigDecimal.setScale()` that use integer constants to specify the rounding mode. Since JDK 1.5, consider using methods that take the `RoundingMode` `enum` parameter instead.\n\n**Example:**\n\n new BigDecimal(\"42\").setScale(2, BigDecimal.ROUND_FLOOR);\n\nAfter the quick-fix is applied:\n\n new BigDecimal(\"42\").setScale(2, RoundingMode.FLOOR);\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "BigDecimalLegacyMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 5", + "index": 53, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DeclareCollectionAsInterface", + "shortDescription": { + "text": "Collection declared by class, not interface" + }, + "fullDescription": { + "text": "Reports declarations of 'Collection' variables made by using the collection class as a type, rather than an appropriate interface. The warning is not issued if weakening the variable type will cause a compilation error. Example: '// Warning: concrete collection class ArrayList used.\n int getTotalLength(ArrayList list) {\n return list.stream().mapToInt(String::length).sum();\n }\n\n // No warning, as trimToSize() method is not\n // available in the List interface\n void addData(ArrayList data) {\n data.add(\"Hello\");\n data.add(\"World\");\n data.trimToSize();\n }' A quick-fix is suggested to use the appropriate collection interface (e.g. 'Collection', 'Set', or 'List').", + "markdown": "Reports declarations of `Collection` variables made by using the collection class as a type, rather than an appropriate interface. The warning is not issued if weakening the variable type will cause a compilation error.\n\nExample:\n\n\n // Warning: concrete collection class ArrayList used.\n int getTotalLength(ArrayList list) {\n return list.stream().mapToInt(String::length).sum();\n }\n\n // No warning, as trimToSize() method is not\n // available in the List interface\n void addData(ArrayList data) {\n data.add(\"Hello\");\n data.add(\"World\");\n data.trimToSize();\n }\n\nA quick-fix is suggested to use the appropriate collection interface (e.g. `Collection`, `Set`, or `List`)." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CollectionDeclaredAsConcreteClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Abstraction issues", + "index": 78, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TrivialStringConcatenation", + "shortDescription": { + "text": "Concatenation with empty string" + }, + "fullDescription": { + "text": "Reports empty string operands in string concatenations. Concatenation with the empty string can be used to convert non-'String' objects or primitives into 'String's, but it can be clearer to use a 'String.valueOf()' method call. A quick-fix is suggested to simplify the concatenation. Example: 'void foo(int x, int y) {\n String s = \"\" + x + \" ; \" + y;\n }' After the quick-fix is applied: 'void foo(int x, int y) {\n String s = x + \" ; \" + y;\n }' Use the Report only where empty strings can be removed without other changes option to ignore cases cases where removing the empty string will require adding a 'String.valueOf()' conversion of another operand.", + "markdown": "Reports empty string operands in string concatenations. Concatenation with the empty string can be used to convert non-`String` objects or primitives into `String`s, but it can be clearer to use a `String.valueOf()` method call.\n\n\nA quick-fix is suggested to simplify the concatenation.\n\n**Example:**\n\n\n void foo(int x, int y) {\n String s = \"\" + x + \" ; \" + y;\n }\n\nAfter the quick-fix is applied:\n\n\n void foo(int x, int y) {\n String s = x + \" ; \" + y;\n }\n\n\nUse the **Report only where empty strings can be removed without other changes**\noption to ignore cases cases where removing the empty string\nwill require adding a `String.valueOf()` conversion of another operand." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ConcatenationWithEmptyString", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspiciousInvocationHandlerImplementation", + "shortDescription": { + "text": "Suspicious 'InvocationHandler' implementation" + }, + "fullDescription": { + "text": "Reports implementations of 'InvocationHandler' that do not proxy standard 'Object' methods like 'hashCode()', 'equals()', and 'toString()'. Failing to handle these methods might cause unexpected problems upon calling them on a proxy instance. Example: 'InvocationHandler myHandler = (proxy, method, params) -> {\n System.out.println(\"Hello World!\");\n return null;\n };\n Runnable myProxy = (Runnable) Proxy.newProxyInstance(\n Thread.currentThread().getContextClassLoader(),\n new Class[] {Runnable.class}, myHandler\n );' This code snippet is designed to only proxy the 'Runnable.run()' method. However, calls to any 'Object' methods, like 'hashCode()', are proxied as well. This can lead to problems like a 'NullPointerException', for example, when adding 'myProxy' to a 'HashSet'. New in 2020.2", + "markdown": "Reports implementations of `InvocationHandler` that do not proxy standard `Object` methods like `hashCode()`, `equals()`, and `toString()`.\n\nFailing to handle these methods might cause unexpected problems upon calling them on a proxy instance.\n\n**Example:**\n\n\n InvocationHandler myHandler = (proxy, method, params) -> {\n System.out.println(\"Hello World!\");\n return null;\n };\n Runnable myProxy = (Runnable) Proxy.newProxyInstance(\n Thread.currentThread().getContextClassLoader(),\n new Class[] {Runnable.class}, myHandler\n );\n\n\nThis code snippet is designed to only proxy the `Runnable.run()` method.\nHowever, calls to any `Object` methods, like `hashCode()`, are proxied as well.\nThis can lead to problems like a `NullPointerException`, for example, when adding `myProxy` to a `HashSet`.\n\nNew in 2020.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SuspiciousInvocationHandlerImplementation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MissingPackageInfo", + "shortDescription": { + "text": "Missing 'package-info.java'" + }, + "fullDescription": { + "text": "Reports packages that contain classes but do not contain the 'package-info.java' or 'package.html' files and are, thus, missing the package documentation. The quick-fix creates an initial 'package-info.java' file.", + "markdown": "Reports packages that contain classes but do not contain the `package-info.java` or `package.html` files and are, thus, missing the package documentation.\n\nThe quick-fix creates an initial `package-info.java` file." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MissingPackageInfo", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Javadoc", + "index": 45, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryConstructor", + "shortDescription": { + "text": "Redundant no-arg constructor" + }, + "fullDescription": { + "text": "Reports unnecessary constructors. A constructor is unnecessary if it is the only constructor of a class, has no parameters, has the same access modifier as its containing class, and does not perform any initialization except explicitly or implicitly calling the superclass constructor without arguments. Such a constructor can be safely removed as it will be generated by the compiler even if not specified. Example: 'public class Foo {\n public Foo() {}\n }' After the quick-fix is applied: 'public class Foo {}' Use the inspection settings to ignore unnecessary constructors that have an annotation.", + "markdown": "Reports unnecessary constructors.\n\n\nA constructor is unnecessary if it is the only constructor of a class, has no parameters,\nhas the same access modifier as its containing class,\nand does not perform any initialization except explicitly or implicitly calling the superclass constructor without arguments.\nSuch a constructor can be safely removed as it will be generated by the compiler even if not specified.\n\n**Example:**\n\n\n public class Foo {\n public Foo() {}\n }\n\nAfter the quick-fix is applied:\n\n\n public class Foo {}\n\n\nUse the inspection settings to ignore unnecessary constructors that have an annotation." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantNoArgConstructor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "HtmlTagCanBeJavadocTag", + "shortDescription": { + "text": "'...' can be replaced with '{@code ...}'" + }, + "fullDescription": { + "text": "Reports usages of '' tags in Javadoc comments. Since Java 5, these tags can be replaced with '{@code ...}' constructs. This allows using angle brackets '<' and '>' inside the comment instead of HTML character entities. Example: '/**\n * @return empty ArrayList<Integer>\n */\n List getList(){ ... }' After the quick-fix is applied: '/**\n * @return empty {@code ArrayList}\n */\n List getList(){ ... }'", + "markdown": "Reports usages of `` tags in Javadoc comments. Since Java 5, these tags can be replaced with `{@code ...}` constructs. This allows using angle brackets `<` and `>` inside the comment instead of HTML character entities.\n\n**Example:**\n\n\n /**\n * @return empty ArrayList<Integer>\n */\n List getList(){ ... }\n\nAfter the quick-fix is applied:\n\n\n /**\n * @return empty {@code ArrayList}\n */\n List getList(){ ... }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "HtmlTagCanBeJavadocTag", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Javadoc", + "index": 45, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringBufferField", + "shortDescription": { + "text": "'StringBuilder' field" + }, + "fullDescription": { + "text": "Reports fields of type 'java.lang.StringBuffer' or 'java.lang.StringBuilder'. Such fields can grow without limit and are often the cause of memory leaks. Example: 'public class Example {\n private StringBuilder builder = new StringBuilder();\n\n }'", + "markdown": "Reports fields of type `java.lang.StringBuffer` or `java.lang.StringBuilder`. Such fields can grow without limit and are often the cause of memory leaks.\n\n**Example:**\n\n\n public class Example {\n private StringBuilder builder = new StringBuilder();\n\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StringBufferField", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Memory", + "index": 73, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassEscapesItsScope", + "shortDescription": { + "text": "Class is exposed outside of its visibility scope" + }, + "fullDescription": { + "text": "Reports usages of classes in a field or method signature where the class has less visibility than the member that uses it. While legal Java, such members cannot be used outside of the visibility scope of the class type they reference. Example: 'public class Parent {\n public Child getChild() {\n return new Child();\n }\n\n private class Child {}\n }' Additionally, in Java 9 and higher, a module may hide some of its classes from other modules by not exporting their packages. However, if a member that is part of the exported API references a non-exported class in its signature, such a member cannot be used outside of the module. Configure the inspection: Use the Report non-exported classes exposed in module API (Java 9+) option to report module API members that expose non-exported classes. Note that the language level of the project or module needs to be 9 or higher for this option. Use the Report non-accessible classes exposed in public API option to report on public members that expose classes with a smaller visibility scope. Use the Report private classes exposed in package-local API option to report on package-local members that expose 'private' classes.", + "markdown": "Reports usages of classes in a field or method signature where the class has less visibility than the member that uses it. While legal Java, such members cannot be used outside of the visibility scope of the class type they reference.\n\n**Example:**\n\n\n public class Parent {\n public Child getChild() {\n return new Child();\n }\n\n private class Child {}\n }\n\n\nAdditionally, in Java 9 and higher, a module may hide some of its classes from other modules by not exporting their packages.\nHowever, if a member that is part of the exported API references a non-exported class in its signature,\nsuch a member cannot be used outside of the module.\n\nConfigure the inspection:\n\n* Use the **Report non-exported classes exposed in module API (Java 9+)** option to report module API members that expose non-exported classes. \n Note that the language level of the project or module needs to be 9 or higher for this option.\n* Use the **Report non-accessible classes exposed in public API** option to report on public members that expose classes with a smaller visibility scope.\n* Use the **Report private classes exposed in package-local API** option to report on package-local members that expose `private` classes." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ClassEscapesDefinedScope", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Visibility", + "index": 57, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantMethodOverride", + "shortDescription": { + "text": "Method is identical to its super method" + }, + "fullDescription": { + "text": "Reports methods that are identical to their super methods. Such methods have the same signature as their super method and either have an identical body or only their body consists only of a call to the super method. These methods are redundant and can be removed. Use the first checkbox below to run the inspection for the methods that override library methods. Checking library methods may slow down the inspection. Use the second checkbox below to ignore methods that only delegate calls to their super methods.", + "markdown": "Reports methods that are identical to their super methods. Such methods have the same signature as their super method and either have an identical body or only their body consists only of a call to the super method. These methods are redundant and can be removed.\n\n\nUse the first checkbox below to run the inspection for the methods that override library methods.\nChecking library methods may slow down the inspection.\n\n\nUse the second checkbox below to ignore methods that only delegate calls to their super methods." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantMethodOverride", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Inheritance issues", + "index": 25, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassNameSameAsAncestorName", + "shortDescription": { + "text": "Class name same as ancestor name" + }, + "fullDescription": { + "text": "Reports classes that have the same name as one of their superclasses, while their fully qualified names remain different. Such class names may be very confusing. Example: 'package util;\n abstract class Iterable implements java.lang.Iterable {}' A quick-fix that renames such classes is available only in the editor.", + "markdown": "Reports classes that have the same name as one of their superclasses, while their fully qualified names remain different. Such class names may be very confusing.\n\n**Example:**\n\n\n package util;\n abstract class Iterable implements java.lang.Iterable {}\n\nA quick-fix that renames such classes is available only in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassNameSameAsAncestorName", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions/Class", + "index": 71, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EqualsUsesNonFinalVariable", + "shortDescription": { + "text": "Non-final field referenced in 'equals()'" + }, + "fullDescription": { + "text": "Reports implementations of 'equals()' that access non-'final' variables. Such access may result in 'equals()' returning different results at different points in the object's lifecycle, which may in turn cause problems when using the standard collections classes. Example: 'public class Person {\n private String lastName;\n\n @Override\n public boolean equals(Object obj) {\n ...\n Person other = (Person) obj;\n if (lastName == null) {\n if (!lastName.equals(other.lastName)) {\n return false;\n ...\n }\n }\n }'", + "markdown": "Reports implementations of `equals()` that access non-`final` variables. Such access may result in `equals()` returning different results at different points in the object's lifecycle, which may in turn cause problems when using the standard collections classes.\n\n**Example:**\n\n\n public class Person {\n private String lastName;\n\n @Override\n public boolean equals(Object obj) {\n ...\n Person other = (Person) obj;\n if (lastName == null) {\n if (!lastName.equals(other.lastName)) {\n return false;\n ...\n }\n }\n }\n \n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonFinalFieldReferenceInEquals", + "cweIds": [ + 697 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ContinueStatementWithLabel", + "shortDescription": { + "text": "'continue' statement with label" + }, + "fullDescription": { + "text": "Reports 'continue' statements with labels. Labeled 'continue' statements complicate refactoring and can be confusing. Example: 'void handle(List strs) {\n outer:\n for (String s: strs) {\n for (char ch : s.toCharArray()) {\n if ('s' == ch) continue outer;\n handleChar(ch);\n }\n }\n }'", + "markdown": "Reports `continue` statements with labels.\n\nLabeled `continue` statements complicate refactoring and can be confusing.\n\nExample:\n\n\n void handle(List strs) {\n outer:\n for (String s: strs) {\n for (char ch : s.toCharArray()) {\n if ('s' == ch) continue outer;\n handleChar(ch);\n }\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ContinueStatementWithLabel", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NestedAssignment", + "shortDescription": { + "text": "Nested assignment" + }, + "fullDescription": { + "text": "Reports assignment expressions that are nested inside other expressions. Such expressions may be confusing and violate the general design principle, which states that any construct should do precisely one thing. Example: 'String userName;\n // Warning: result of assignment to 'userName' is used\n String message = \"Hello \" + (userName = \"Alice\") + \"!\"\n System.out.println(message);\n System.out.println(\"Goodbye \" + userName);'", + "markdown": "Reports assignment expressions that are nested inside other expressions.\n\nSuch expressions may be confusing and violate the general design principle, which states that any construct should do precisely one thing.\n\n**Example:**\n\n\n String userName;\n // Warning: result of assignment to 'userName' is used\n String message = \"Hello \" + (userName = \"Alice\") + \"!\"\n System.out.println(message);\n System.out.println(\"Goodbye \" + userName);\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NestedAssignment", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Assignment issues", + "index": 34, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AutoUnboxing", + "shortDescription": { + "text": "Auto-unboxing" + }, + "fullDescription": { + "text": "Reports expressions that are affected by unboxing conversion (automatic unwrapping of objects into primitive values). Try not to use objects instead of primitives. It might significantly affect the performance. Example: 'int x = new Integer(42);' The quick-fix makes the conversion explicit: 'int x = new Integer(42).intValue();' AutoUnboxing appeared in Java 5. This inspection can help to downgrade for backward compatibility with earlier Java versions.", + "markdown": "Reports expressions that are affected by unboxing conversion (automatic unwrapping of objects into primitive values). Try not to use objects instead of primitives. It might significantly affect the performance.\n\n**Example:**\n\n int x = new Integer(42);\n\nThe quick-fix makes the conversion explicit:\n\n int x = new Integer(42).intValue();\n\n\n*AutoUnboxing* appeared in Java 5.\nThis inspection can help to downgrade for backward compatibility with earlier Java versions." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AutoUnboxing", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SimplifiableBooleanExpression", + "shortDescription": { + "text": "Simplifiable boolean expression" + }, + "fullDescription": { + "text": "Reports boolean expressions that can be simplified. Example: 'void f(boolean foo, boolean bar) {\n boolean b = !(foo ^ bar);\n }' After the quick-fix is applied: 'void f(boolean foo, boolean bar) {\n boolean b = foo == bar;\n }' Example: 'void f(boolean foo, boolean bar) {\n boolean b = (foo && bar) || !foo;\n }' After the quick-fix is applied: 'void f(boolean foo, boolean bar) {\n boolean b = !foo || bar;\n }'", + "markdown": "Reports boolean expressions that can be simplified.\n\nExample:\n\n\n void f(boolean foo, boolean bar) {\n boolean b = !(foo ^ bar);\n }\n\nAfter the quick-fix is applied:\n\n\n void f(boolean foo, boolean bar) {\n boolean b = foo == bar;\n }\n\nExample:\n\n\n void f(boolean foo, boolean bar) {\n boolean b = (foo && bar) || !foo;\n }\n \nAfter the quick-fix is applied:\n\n\n void f(boolean foo, boolean bar) {\n boolean b = !foo || bar;\n }\n \n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SimplifiableBooleanExpression", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringConcatenationInMessageFormatCall", + "shortDescription": { + "text": "String concatenation as argument to 'MessageFormat.format()' call" + }, + "fullDescription": { + "text": "Reports non-constant string concatenations used as an argument to a call to 'MessageFormat.format()'. While occasionally intended, this is usually a misuse of the formatting method and may even cause unexpected exceptions if the variables used in the concatenated string contain special characters like '{'. Also, sometimes this could be the result of mistakenly concatenating a string format argument by typing a '+' when a ',' was meant. Example: 'String formatGreeting(String userName, int balance) {\n return MessageFormat.format(\"Hello, \" + userName + \"! Your balance is {0}.\", balance);\n }' Here, the 'userName' will be interpreted as a part of the format string, which may result in 'IllegalArgumentException' (for example, if 'userName' is '\"{\"'). This call should be probably replaced with 'MessageFormat.format(\"Hello, {0}! Your balance is {1}.\", userName, balance)'.", + "markdown": "Reports non-constant string concatenations used as an argument to a call to `MessageFormat.format()`.\n\n\nWhile occasionally intended, this is usually a misuse of the formatting method\nand may even cause unexpected exceptions if the variables used in the concatenated string contain\nspecial characters like `{`.\n\n\nAlso, sometimes this could be the result\nof mistakenly concatenating a string format argument by typing a `+` when a `,` was meant.\n\n**Example:**\n\n\n String formatGreeting(String userName, int balance) {\n return MessageFormat.format(\"Hello, \" + userName + \"! Your balance is {0}.\", balance);\n }\n\n\nHere, the `userName` will be interpreted as a part of the format string, which may result\nin `IllegalArgumentException` (for example, if `userName` is `\"{\"`).\nThis call should be probably replaced with `MessageFormat.format(\"Hello, {0}! Your balance is {1}.\", userName, balance)`." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StringConcatenationInMessageFormatCall", + "cweIds": [ + 116, + 134 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonFinalFieldInImmutable", + "shortDescription": { + "text": "Non-final field in '@Immutable' class" + }, + "fullDescription": { + "text": "Reports any non-final field in a class with the '@Immutable' annotation. This violates the contract of the '@Immutable' annotation. Example: 'import javax.annotation.concurrent.Immutable;\n @Immutable\n class Foo {\n String bar = \"foo\";\n }' Supported '@GuardedBy' annotations are: 'net.jcip.annotations.GuardedBy' 'javax.annotation.concurrent.GuardedBy' 'org.apache.http.annotation.GuardedBy' 'com.android.annotations.concurrency.GuardedBy' 'androidx.annotation.GuardedBy' 'com.google.errorprone.annotations.concurrent.GuardedBy'", + "markdown": "Reports any non-final field in a class with the `@Immutable` annotation. This violates the contract of the `@Immutable` annotation.\n\nExample:\n\n\n import javax.annotation.concurrent.Immutable;\n @Immutable\n class Foo {\n String bar = \"foo\";\n }\n\nSupported `@GuardedBy` annotations are:\n\n* `net.jcip.annotations.GuardedBy`\n* `javax.annotation.concurrent.GuardedBy`\n* `org.apache.http.annotation.GuardedBy`\n* `com.android.annotations.concurrency.GuardedBy`\n* `androidx.annotation.GuardedBy`\n* `com.google.errorprone.annotations.concurrent.GuardedBy`" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonFinalFieldInImmutable", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Concurrency annotation issues", + "index": 61, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CallToStringConcatCanBeReplacedByOperator", + "shortDescription": { + "text": "Call to 'String.concat()' can be replaced with '+'" + }, + "fullDescription": { + "text": "Reports calls to 'java.lang.String.concat()'. Such calls can be replaced with the '+' operator for clarity and possible increased performance if the method was invoked on a constant with a constant argument. Example: 'String foo(String name) {\n return name.concat(\"foo\");\n }' After the quick-fix is applied: 'String foo(String name) {\n return name + \"foo\";\n }'", + "markdown": "Reports calls to `java.lang.String.concat()`.\n\n\nSuch calls can be replaced with the `+` operator for clarity and possible increased\nperformance if the method was invoked on a constant with a constant argument.\n\n**Example:**\n\n\n String foo(String name) {\n return name.concat(\"foo\");\n }\n\nAfter the quick-fix is applied:\n\n\n String foo(String name) {\n return name + \"foo\";\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToStringConcatCanBeReplacedByOperator", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ExpectedExceptionNeverThrown", + "shortDescription": { + "text": "Expected exception never thrown in test method body" + }, + "fullDescription": { + "text": "Reports checked exceptions expected by a JUnit 4 test-method that are never thrown inside the method body. Such test methods will never succeed. Example: '@Test(expected = CloneNotSupportedException.class)\n public void testIt() {\n }'", + "markdown": "Reports checked exceptions expected by a JUnit 4 test-method that are never thrown inside the method body. Such test methods will never succeed.\n\n**Example:**\n\n\n @Test(expected = CloneNotSupportedException.class)\n public void testIt() {\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ExpectedExceptionNeverThrown", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/JUnit", + "index": 77, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SignalWithoutCorrespondingAwait", + "shortDescription": { + "text": "'signal()' without corresponding 'await()'" + }, + "fullDescription": { + "text": "Reports calls to 'Condition.signal()' or 'Condition.signalAll()' for which no call to a corresponding 'Condition.await()' can be found. Only calls that target fields of the current class are reported by this inspection. Example: 'class Queue {\n private final Condition isEmpty = ...;\n\n void add(Object elem) {\n // ...\n isEmpty.signal(); // warning: Call to 'signal()' without corresponding 'await()'\n // ...\n }\n\n void remove(Object elem) throws InterruptedException {\n // ...\n // isEmpty.await();\n // ...\n }\n }'", + "markdown": "Reports calls to `Condition.signal()` or `Condition.signalAll()` for which no call to a corresponding `Condition.await()` can be found.\n\nOnly calls that target fields of the current class are reported by this inspection.\n\n**Example:**\n\n\n class Queue {\n private final Condition isEmpty = ...;\n\n void add(Object elem) {\n // ...\n isEmpty.signal(); // warning: Call to 'signal()' without corresponding 'await()'\n // ...\n }\n\n void remove(Object elem) throws InterruptedException {\n // ...\n // isEmpty.await();\n // ...\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SignalWithoutCorrespondingAwait", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FoldExpressionIntoStream", + "shortDescription": { + "text": "Expression can be folded into Stream chain" + }, + "fullDescription": { + "text": "Reports expressions with a repeating pattern which could be replaced with Stream API or 'String.join()'. Example: 'boolean allStartWith(String a, String b, String c, String d, String prefix) {\n return a.startsWith(prefix) && b.startsWith(prefix) && c.startsWith(prefix) && d.startsWith(prefix);\n }' After the quick-fix is applied: 'boolean foo(String a, String b, String c, String d, String prefix) {\n return Stream.of(a, b, c, d).allMatch(s -> s.startsWith(prefix));\n }' Example: 'String joinAll(String a, String b, String c, String d) {\n return a + \",\" + b + \",\" + c + \",\" + d;\n }' After the quick-fix is applied: 'String joinAll(String a, String b, String c, String d) {\n return String.join(\",\", a, b, c, d);\n }' This inspection only reports if the language level of the project or module is 8 or higher. New in 2018.2", + "markdown": "Reports expressions with a repeating pattern which could be replaced with *Stream API* or `String.join()`.\n\nExample:\n\n\n boolean allStartWith(String a, String b, String c, String d, String prefix) {\n return a.startsWith(prefix) && b.startsWith(prefix) && c.startsWith(prefix) && d.startsWith(prefix);\n }\n\nAfter the quick-fix is applied:\n\n\n boolean foo(String a, String b, String c, String d, String prefix) {\n return Stream.of(a, b, c, d).allMatch(s -> s.startsWith(prefix));\n }\n\nExample:\n\n\n String joinAll(String a, String b, String c, String d) {\n return a + \",\" + b + \",\" + c + \",\" + d;\n }\n\nAfter the quick-fix is applied:\n\n\n String joinAll(String a, String b, String c, String d) {\n return String.join(\",\", a, b, c, d);\n }\n\nThis inspection only reports if the language level of the project or module is 8 or higher.\n\nNew in 2018.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "FoldExpressionIntoStream", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 8", + "index": 62, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "HardcodedFileSeparators", + "shortDescription": { + "text": "Hardcoded file separator" + }, + "fullDescription": { + "text": "Reports the forward ('/') or backward ('\\') slash in a string or character literal. These characters are commonly used as file separators, and portability may suffer if they are hardcoded. The inspection will not report backward slashes inside escape sequences and forward slashes immediately following the '<' character or immediately preceding the '>' character, as those often indicate XML or HTML tags rather than file names. Strings representing a 'java.util.TimeZone' ID, strings that are valid regular expressions, or strings that equal IANA-registered MIME media types will not be reported either. Example: 'new File(\"C:\\\\Users\\\\Name\");' Use the option to include 'example/*' in the set of recognized media types. Normally, usage of the 'example/*' MIME media type outside of an example (e.g. in a 'Content-Type' header) is an error.", + "markdown": "Reports the forward (`/`) or backward (`\\`) slash in a string or character literal. These characters are commonly used as file separators, and portability may suffer if they are hardcoded.\n\n\nThe inspection will not report backward slashes inside escape sequences and forward slashes immediately following the '\\<' character\nor immediately preceding the '\\>' character, as those often indicate XML or HTML tags rather than file names.\nStrings representing a `java.util.TimeZone` ID, strings that are valid regular expressions,\nor strings that equal IANA-registered MIME media types will not be reported either.\n\n**Example:**\n\n\n new File(\"C:\\\\Users\\\\Name\");\n\n\nUse the option to include `example/*` in the set of recognized media types.\nNormally, usage of the `example/*` MIME media type outside of an example (e.g. in a `Content-Type`\nheader) is an error." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "HardcodedFileSeparator", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Portability", + "index": 7, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SerializableDeserializableClassInSecureContext", + "shortDescription": { + "text": "Serializable class in secure context" + }, + "fullDescription": { + "text": "Reports classes that may be serialized or deserialized. A class may be serialized if it supports the 'Serializable' interface, and its 'readObject()' and 'writeObject()' methods are not defined to always throw an exception. Serializable classes may be dangerous in code intended for secure use. Example: 'class DeserializableClass implements Serializable { // the class doesn't contain 'writeObject()' method throwing an exception\n private int sensitive = 736326;\n\n private void readObject(ObjectInputStream in) {\n throw new Error();\n }\n}' After the quick-fix is applied: 'class DeserializableClass implements Serializable {\n private int sensitive = 736326;\n\n private void readObject(ObjectInputStream in) {\n throw new Error();\n }\n\n private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {\n throw new java.io.NotSerializableException(\"DeserializableClass\");\n }\n }' Use the following options to configure the inspection: List classes whose inheritors should not be reported by this inspection. This is meant for classes that inherit 'Serializable' from a superclass but are not intended for serialization. Note that it still may be more secure to add 'readObject()' and 'writeObject()' methods which always throw an exception, instead of ignoring those classes. Whether to ignore serializable anonymous classes.", + "markdown": "Reports classes that may be serialized or deserialized.\n\n\nA class may be serialized if it supports the `Serializable` interface,\nand its `readObject()` and `writeObject()` methods are not defined to always\nthrow an exception. Serializable classes may be dangerous in code intended for secure use.\n\n**Example:**\n\n\n class DeserializableClass implements Serializable { // the class doesn't contain 'writeObject()' method throwing an exception\n private int sensitive = 736326;\n\n private void readObject(ObjectInputStream in) {\n throw new Error();\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class DeserializableClass implements Serializable {\n private int sensitive = 736326;\n\n private void readObject(ObjectInputStream in) {\n throw new Error();\n }\n\n private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {\n throw new java.io.NotSerializableException(\"DeserializableClass\");\n }\n }\n\n\nUse the following options to configure the inspection:\n\n* List classes whose inheritors should not be reported by this inspection. This is meant for classes that inherit `Serializable` from a superclass but are not intended for serialization. Note that it still may be more secure to add `readObject()` and `writeObject()` methods which always throw an exception, instead of ignoring those classes.\n* Whether to ignore serializable anonymous classes." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SerializableDeserializableClassInSecureContext", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Security", + "index": 30, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConfusingFloatingPointLiteral", + "shortDescription": { + "text": "Confusing floating-point literal" + }, + "fullDescription": { + "text": "Reports any floating point numbers that don't have a decimal point, numbers before the decimal point, or numbers after the decimal point. Such literals may be confusing, and violate several coding standards. Example: 'double d = .03;' After the quick-fix is applied: 'double d = 0.03;' Use the Ignore floating point literals in scientific notation option to ignore floating point numbers in scientific notation.", + "markdown": "Reports any floating point numbers that don't have a decimal point, numbers before the decimal point, or numbers after the decimal point.\n\nSuch literals may be confusing, and violate several coding standards.\n\n**Example:**\n\n double d = .03;\n\nAfter the quick-fix is applied:\n\n double d = 0.03;\n\n\nUse the **Ignore floating point literals in scientific notation** option to ignore floating point numbers in scientific notation." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ConfusingFloatingPointLiteral", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JavaModuleNaming", + "shortDescription": { + "text": "Java module name contradicts the convention" + }, + "fullDescription": { + "text": "Reports cases when a module name contradicts Java Platform Module System recommendations. One of the recommendations is to avoid using digits at the end of module names. Example: 'module foo1.bar2 {}'", + "markdown": "Reports cases when a module name contradicts Java Platform Module System recommendations.\n\nOne of the [recommendations](http://mail.openjdk.org/pipermail/jpms-spec-experts/2017-March/000659.html)\nis to avoid using digits at the end of module names.\n\n**Example:**\n\n\n module foo1.bar2 {}\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "JavaModuleNaming", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions", + "index": 51, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JavadocReference", + "shortDescription": { + "text": "Declaration has problems in Javadoc references" + }, + "fullDescription": { + "text": "Reports unresolved references inside Javadoc comments. In the following example, the 'someParam' parameter is missing, so it will be highlighted: 'class A {\n /**\n * @param someParam description\n **/\n void foo() {\n }\n}' Disable the Report inaccessible symbols option to ignore the tags that reference missing method parameters, classes, fields and methods.", + "markdown": "Reports unresolved references inside Javadoc comments.\n\nIn the following example, the `someParam` parameter is missing, so it will be highlighted:\n\n\n class A {\n /**\n * @param someParam description\n **/\n void foo() {\n }\n }\n\n\nDisable the **Report inaccessible symbols** option to ignore the tags that reference missing method parameters,\nclasses, fields and methods." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "JavadocReference", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Javadoc", + "index": 45, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnaryPlus", + "shortDescription": { + "text": "Unary plus" + }, + "fullDescription": { + "text": "Reports usages of the '+' unary operator. The unary plus is usually a null operation, and its presence might represent a coding error. For example, in a combination with the increment operator (like in '+++') or with the equal operator (like in '=+'). Example: 'void unaryPlus(int i) {\n int x = + +i;\n }' The following quick fixes are suggested: Remove '+' operators before the 'i' variable: 'void unaryPlus(int i) {\n int x = i;\n }' Replace '+' operators with the prefix increment operator: 'void unaryPlus(int i) {\n int x = ++i;\n }' Use the checkbox below to report unary pluses that are used together with a binary or another unary expression. It means the inspection will not report situations when a unary plus expression is used in array initializer expressions or as a method argument.", + "markdown": "Reports usages of the `+` unary operator. The unary plus is usually a null operation, and its presence might represent a coding error. For example, in a combination with the increment operator (like in `+++`) or with the equal operator (like in `=+`).\n\n**Example:**\n\n\n void unaryPlus(int i) {\n int x = + +i;\n }\n\nThe following quick fixes are suggested:\n\n* Remove `+` operators before the `i` variable:\n\n\n void unaryPlus(int i) {\n int x = i;\n }\n\n* Replace `+` operators with the prefix increment operator:\n\n\n void unaryPlus(int i) {\n int x = ++i;\n }\n\n\nUse the checkbox below to report unary pluses that are used together with a binary or another unary expression.\nIt means the inspection will not report situations when a unary plus expression is used in array\ninitializer expressions or as a method argument." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "UnaryPlus", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConstructorCount", + "shortDescription": { + "text": "Class with too many constructors" + }, + "fullDescription": { + "text": "Reports classes whose number of constructors exceeds the specified maximum. Classes with too many constructors are prone to initialization errors, and often modeling such a class as multiple subclasses is preferable. Configure the inspection: Use the Constructor count limit field to specify the maximum allowed number of constructors in a class. Use the Ignore deprecated constructors option to avoid adding deprecated constructors to the total count.", + "markdown": "Reports classes whose number of constructors exceeds the specified maximum.\n\nClasses with too many constructors are prone to initialization errors, and often modeling such a class as multiple subclasses is preferable.\n\nConfigure the inspection:\n\n* Use the **Constructor count limit** field to specify the maximum allowed number of constructors in a class.\n* Use the **Ignore deprecated constructors** option to avoid adding deprecated constructors to the total count." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassWithTooManyConstructors", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class metrics", + "index": 87, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MethodOverloadsParentMethod", + "shortDescription": { + "text": "Possibly unintended overload of method from superclass" + }, + "fullDescription": { + "text": "Reports instance methods with the same name and the same number of parameters as a method in a superclass, but where at least one of the parameters is of a different incompatible type. In this case, the method in a subclass will be overloading the method from the superclass instead of overriding it. If it is unintended, it may result in latent bugs. Example: 'public class Foo {\n void foo(int x) {}\n }\n\n public class Bar extends Foo {\n void foo(Number x) {} // Method 'foo()' overloads a compatible method of a superclass,\n // when overriding might have been intended\n }' Use the option to choose whether the inspection should also report cases where parameter types are not compatible.", + "markdown": "Reports instance methods with the same name and the same number of parameters as a method in a superclass, but where at least one of the parameters is of a different incompatible type.\n\n\nIn this case, the method in a subclass will be overloading the method from the superclass\ninstead of overriding it. If it is unintended, it may result in latent bugs.\n\n**Example:**\n\n\n public class Foo {\n void foo(int x) {}\n }\n\n public class Bar extends Foo {\n void foo(Number x) {} // Method 'foo()' overloads a compatible method of a superclass,\n // when overriding might have been intended\n }\n\n\nUse the option to choose whether the inspection should also report cases where parameter types are not compatible." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MethodOverloadsMethodOfSuperclass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Visibility", + "index": 57, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IncrementDecrementUsedAsExpression", + "shortDescription": { + "text": "Result of '++' or '--' used" + }, + "fullDescription": { + "text": "Reports increment or decrement expressions that are nested inside other expressions. Such expressions may be confusing and violate the general design principle, which states that any construct should do precisely one thing. The quick-fix extracts the increment or decrement operation to a separate expression statement. Example: 'int i = 10;\n while (i-- > 0) {\n System.out.println(i);\n }' After the quick-fix is applied: 'int i = 10;\n while (i > 0) {\n i--;\n System.out.println(i);\n }\n i--;'", + "markdown": "Reports increment or decrement expressions that are nested inside other expressions. Such expressions may be confusing and violate the general design principle, which states that any construct should do precisely one thing.\n\nThe quick-fix extracts the increment or decrement operation to a separate expression statement.\n\n**Example:**\n\n\n int i = 10;\n while (i-- > 0) {\n System.out.println(i);\n }\n\nAfter the quick-fix is applied:\n\n\n int i = 10;\n while (i > 0) {\n i--;\n System.out.println(i);\n }\n i--;\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ValueOfIncrementOrDecrementUsed", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Assignment issues", + "index": 34, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DisjointPackage", + "shortDescription": { + "text": "Package with disjoint dependency graph" + }, + "fullDescription": { + "text": "Reports packages whose classes can be separated into mutually independent subsets. Such disjoint packages indicate ad-hoc packaging or a lack of conceptual cohesion. Available only from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name and isn't reported in the editor.", + "markdown": "Reports packages whose classes can be separated into mutually independent subsets.\n\nSuch disjoint packages indicate ad-hoc packaging or a lack of conceptual cohesion.\n\nAvailable only from **Code \\| Inspect Code** or\n**Code \\| Analyze Code \\| Run Inspection by Name** and isn't reported in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "DisjointPackage", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Packaging issues", + "index": 36, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MethodMayBeSynchronized", + "shortDescription": { + "text": "Method with single 'synchronized' block can be replaced with 'synchronized' method" + }, + "fullDescription": { + "text": "Reports methods whose body contains a single 'synchronized' statement. A lock expression for this 'synchronized' statement must be equal to 'this' for instance methods or '[ClassName].class' for static methods. To improve readability of such methods, you can remove the 'synchronized' wrapper and mark the method as 'synchronized'. Example: 'public int generateInt(int x) {\n synchronized (this) {\n return 1;\n }\n }' After the quick-fix is applied: 'public synchronized int generateInt(int x) {\n return 1;\n }'", + "markdown": "Reports methods whose body contains a single `synchronized` statement. A lock expression for this `synchronized` statement must be equal to `this` for instance methods or `[ClassName].class` for static methods.\n\n\nTo improve readability of such methods,\nyou can remove the `synchronized` wrapper and mark the method as `synchronized`.\n\n**Example:**\n\n\n public int generateInt(int x) {\n synchronized (this) {\n return 1;\n }\n }\n\nAfter the quick-fix is applied:\n\n\n public synchronized int generateInt(int x) {\n return 1;\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MethodMayBeSynchronized", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UpperCaseFieldNameNotConstant", + "shortDescription": { + "text": "Non-constant field with upper-case name" + }, + "fullDescription": { + "text": "Reports non-'static' non-'final' fields whose names are all in upper case. Such fields may cause confusion by breaking a common naming convention and are often used by mistake. Example: 'public static int THE_ANSWER = 42; //a warning here: final modifier is missing' A quick-fix that renames such fields is available only in the editor.", + "markdown": "Reports non-`static` non-`final` fields whose names are all in upper case.\n\nSuch fields may cause confusion by breaking a common naming convention and\nare often used by mistake.\n\n**Example:**\n\n\n public static int THE_ANSWER = 42; //a warning here: final modifier is missing\n\nA quick-fix that renames such fields is available only in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonConstantFieldWithUpperCaseName", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions", + "index": 51, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LoggingPlaceholderCountMatchesArgumentCount", + "shortDescription": { + "text": "Number of placeholders does not match number of arguments in logging call" + }, + "fullDescription": { + "text": "Reports SLF4J, Log4j2 and akka.event.LoggingAdapter logging calls, such as 'logger.info(\"{}: {}\", key)' where the number of '{}' placeholders in the logger message doesn't match the number of other arguments to the logging call. Use the inspection option to specify which implementation SLF4J uses. If Check automatically is chosen, then 'org.apache.logging.slf4j.Log4jLogger' is searched in the classpath. If this file is founded or Yes is chosen, then cases, when the last parameter with an exception type has a placeholder, will not be reported for SLFJ4 API. For example: '//this case will not be reported with \"Yes\" option\nlog.error(\"For id {}: {}\", \"1\", new RuntimeException());' In this case 'new RuntimeException()' will be printed using 'toString()', (its stacktrace will not be printed): 'For id 1: java.lang.RuntimeException' Otherwise, it will be highlighted because the last placeholder is not used: 'For id 1: {}\njava.lang.RuntimeException: null' No option can be used to always highlight such cases when a placeholder is used for an exception even if 'org.apache.logging.slf4j.Log4jLogger' is used as a backend. This option works only for SLF4J.", + "markdown": "Reports SLF4J, Log4j2 and akka.event.LoggingAdapter logging calls, such as `logger.info(\"{}: {}\", key)` where the number of `{}` placeholders in the logger message doesn't match the number of other arguments to the logging call.\n\n\nUse the inspection option to specify which implementation SLF4J uses.\nIf **Check automatically** is chosen, then `org.apache.logging.slf4j.Log4jLogger` is searched in the classpath.\nIf this file is founded or **Yes** is chosen, then cases, when the last parameter with an exception type has a placeholder,\nwill not be reported for SLFJ4 API. \n\nFor example:\n\n\n //this case will not be reported with \"Yes\" option\n log.error(\"For id {}: {}\", \"1\", new RuntimeException());\n\nIn this case 'new RuntimeException()' will be printed using 'toString()', (its stacktrace will not be printed):\n\n\n For id 1: java.lang.RuntimeException\n\nOtherwise, it will be highlighted because the last placeholder is not used:\n\n\n For id 1: {}\n java.lang.RuntimeException: null\n\n**No** option can be used to always highlight such cases when a placeholder is used for an exception even if `org.apache.logging.slf4j.Log4jLogger` is used as a backend. \nThis option works only for SLF4J." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "LoggingPlaceholderCountMatchesArgumentCount", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages/Logging", + "index": 46, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Convert2streamapi", + "shortDescription": { + "text": "Loop can be collapsed with Stream API" + }, + "fullDescription": { + "text": "Reports loops which can be replaced with stream API calls using lambda expressions. Such a replacement changes the style from imperative to more functional and makes the code more compact. Example: 'boolean check(List data) {\n for (String e : data) {\n String trimmed = e.trim();\n if (!trimmed.startsWith(\"xyz\")) {\n return false;\n }\n }\n return true;\n }' After the quick-fix is applied: 'boolean check(List data) {\n return data.stream().map(String::trim).allMatch(trimmed -> trimmed.startsWith(\"xyz\"));\n }' This inspection only reports if the language level of the project or module is 8 or higher.", + "markdown": "Reports loops which can be replaced with stream API calls using lambda expressions.\n\nSuch a replacement changes the style from imperative to more functional and makes the code more compact.\n\nExample:\n\n\n boolean check(List data) {\n for (String e : data) {\n String trimmed = e.trim();\n if (!trimmed.startsWith(\"xyz\")) {\n return false;\n }\n }\n return true;\n }\n\nAfter the quick-fix is applied:\n\n\n boolean check(List data) {\n return data.stream().map(String::trim).allMatch(trimmed -> trimmed.startsWith(\"xyz\"));\n }\n\nThis inspection only reports if the language level of the project or module is 8 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "Convert2streamapi", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 8", + "index": 62, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SerialPersistentFieldsWithWrongSignature", + "shortDescription": { + "text": "'serialPersistentFields' field not declared 'private static final ObjectStreamField[]'" + }, + "fullDescription": { + "text": "Reports 'Serializable' classes whose 'serialPersistentFields' field is not declared as 'private static final ObjectStreamField[]'. If a 'serialPersistentFields' field is not declared with those modifiers, the serialization behavior will be as if the field was not declared at all. Example: 'class List implements Serializable {\n private List next;\n\n ObjectStreamField[] serialPersistentFields = {new ObjectStreamField(\"next\", List.class)};\n\n }'", + "markdown": "Reports `Serializable` classes whose `serialPersistentFields` field is not declared as `private static final ObjectStreamField[]`.\n\n\nIf a `serialPersistentFields` field is not declared with those modifiers,\nthe serialization behavior will be as if the field was not declared at all.\n\n**Example:**\n\n\n class List implements Serializable {\n private List next;\n\n ObjectStreamField[] serialPersistentFields = {new ObjectStreamField(\"next\", List.class)};\n\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SerialPersistentFieldsWithWrongSignature", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ComparatorResultComparison", + "shortDescription": { + "text": "Suspicious usage of compare method" + }, + "fullDescription": { + "text": "Reports comparisons of the result of 'Comparator.compare()' or 'Comparable.compareTo()' calls with non-zero constants. By contract, these methods can return any integer (not just -1, 0 or 1), so comparing against particular numbers is bad practice. Some widely used comparison methods (e.g. 'String.compareTo()') actually return values outside the [-1..1] range, and such a comparison may cause incorrect program behavior. Example: 'void validate(String s1, String s2) {\n // Comparing to 1 is incorrect\n if (s1.compareTo(s2) == 1) {\n throw new IllegalArgumentException(\"Incorrect order\");\n }\n }' After the quick-fix is applied: 'void validate(String s1, String s2) {\n if (s1.compareTo(s2) > 0) {\n throw new IllegalArgumentException(\"Incorrect order\");\n }\n }' New in 2017.2", + "markdown": "Reports comparisons of the result of `Comparator.compare()` or `Comparable.compareTo()` calls with non-zero constants. By contract, these methods can return any integer (not just -1, 0 or 1), so comparing against particular numbers is bad practice. Some widely used comparison methods (e.g. `String.compareTo()`) actually return values outside the \\[-1..1\\] range, and such a comparison may cause incorrect program behavior.\n\nExample:\n\n\n void validate(String s1, String s2) {\n // Comparing to 1 is incorrect\n if (s1.compareTo(s2) == 1) {\n throw new IllegalArgumentException(\"Incorrect order\");\n }\n }\n\nAfter the quick-fix is applied:\n\n\n void validate(String s1, String s2) {\n if (s1.compareTo(s2) > 0) {\n throw new IllegalArgumentException(\"Incorrect order\");\n }\n }\n\nNew in 2017.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ComparatorResultComparison", + "cweIds": [ + 253 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ListIndexOfReplaceableByContains", + "shortDescription": { + "text": "'List.indexOf()' expression can be replaced with 'contains()'" + }, + "fullDescription": { + "text": "Reports any 'List.indexOf()' expressions that can be replaced with the 'List.contains()' method. Example: 'boolean hasEmptyString(List list) {\n // Warning: can be simplified\n return list.indexOf(\"\") >= 0;\n }' The provided quick-fix replaces the 'indexOf' call with the 'contains' call: 'boolean hasEmptyString(List list) {\n // Quick-fix is applied\n return list.contains(\"\");\n }'", + "markdown": "Reports any `List.indexOf()` expressions that can be replaced with the `List.contains()` method.\n\nExample:\n\n\n boolean hasEmptyString(List list) {\n // Warning: can be simplified\n return list.indexOf(\"\") >= 0;\n }\n\nThe provided quick-fix replaces the `indexOf` call with the `contains` call:\n\n\n boolean hasEmptyString(List list) {\n // Quick-fix is applied\n return list.contains(\"\");\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ListIndexOfReplaceableByContains", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonStrictComparisonCanBeEquality", + "shortDescription": { + "text": "Non-strict inequality '>=' or '<=' can be replaced with '=='" + }, + "fullDescription": { + "text": "Reports inequality conditions that, according to data flow analysis, can be satisfied only for a single operand value. Such conditions could be replaced with equality conditions to make the code clearer. Example: 'if (x >= 10) {\n ...\n if (x <= 10) { // can be replaced with 'x == 10'\n }\n }' New in 2022.2", + "markdown": "Reports inequality conditions that, according to data flow analysis, can be satisfied only for a single operand value. Such conditions could be replaced with equality conditions to make the code clearer.\n\nExample:\n\n\n if (x >= 10) {\n ...\n if (x <= 10) { // can be replaced with 'x == 10'\n }\n }\n\nNew in 2022.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "NonStrictComparisonCanBeEquality", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryParentheses", + "shortDescription": { + "text": "Unnecessary parentheses" + }, + "fullDescription": { + "text": "Reports any instance of unnecessary parentheses. Parentheses are considered unnecessary if the evaluation order of an expression remains unchanged after you remove the parentheses. Example: 'int n = 3 + (9 * 8);' After quick-fix is applied: 'int n = 3 + 9 * 8;' Configure the inspection: Use the Ignore clarifying parentheses option to ignore parentheses that help clarify a binary expression. Parentheses are clarifying if the parenthesized expression is an 'instanceof' expression that is a part of a larger expression or has a different operator than the parent expression. Use the Ignore parentheses around the condition of conditional expressions option to ignore any parentheses around the condition of conditional expressions. Some coding standards specify that all such conditions must be surrounded by parentheses. Use the Ignore parentheses around single no formal type lambda parameter option to ignore parentheses around a single lambda parameter within a lambda expression.", + "markdown": "Reports any instance of unnecessary parentheses.\n\nParentheses are considered unnecessary if the evaluation order of an expression remains\nunchanged after you remove the parentheses.\n\nExample:\n\n\n int n = 3 + (9 * 8);\n\nAfter quick-fix is applied:\n\n\n int n = 3 + 9 * 8;\n\nConfigure the inspection:\n\n* Use the **Ignore clarifying parentheses** option to ignore parentheses that help clarify a binary expression. Parentheses are clarifying if the parenthesized expression is an `instanceof` expression that is a part of a larger expression or has a different operator than the parent expression.\n* Use the **Ignore parentheses around the condition of conditional expressions** option to ignore any parentheses around the condition of conditional expressions. Some coding standards specify that all such conditions must be surrounded by parentheses.\n* Use the **Ignore parentheses around single no formal type lambda parameter** option to ignore parentheses around a single lambda parameter within a lambda expression." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "UnnecessaryParentheses", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ImplicitArrayToString", + "shortDescription": { + "text": "Call to 'toString()' on array" + }, + "fullDescription": { + "text": "Reports arrays used in 'String' concatenations or passed as parameters to 'java.io.PrintStream' methods, such as 'System.out.println()'. Usually, the content of the array is meant to be used and not the array object itself. Example: 'void print(Object[] objects) {\n System.out.println(objects);\n }' After the quick-fix is applied: 'void print(Object[] objects) {\n System.out.println(Arrays.toString(objects));\n }'", + "markdown": "Reports arrays used in `String` concatenations or passed as parameters to `java.io.PrintStream` methods, such as `System.out.println()`.\n\n\nUsually, the content of the array is meant to be used and not the array object itself.\n\n**Example:**\n\n\n void print(Object[] objects) {\n System.out.println(objects);\n }\n\nAfter the quick-fix is applied:\n\n\n void print(Object[] objects) {\n System.out.println(Arrays.toString(objects));\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ImplicitArrayToString", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReuseOfLocalVariable", + "shortDescription": { + "text": "Reuse of local variable" + }, + "fullDescription": { + "text": "Reports local variables that are \"reused\" overwriting their values with new values unrelated to their original use. Such a local variable reuse may be confusing, as the intended semantics of the local variable may vary with each use. It may also be prone to bugs if due to the code changes, the values that have been considered overwritten actually appear to be alive. It is a good practice to keep variable lifetimes as short as possible, and not to reuse local variables for the sake of brevity. Example: 'void x() {\n String s = \"one\";\n System.out.println(\"s = \" + s);\n s = \"two\"; //reuse of local variable 's'\n System.out.println(\"s = \" + s);\n }'", + "markdown": "Reports local variables that are \"reused\" overwriting their values with new values unrelated to their original use.\n\nSuch a local variable reuse may be confusing,\nas the intended semantics of the local variable may vary with each use. It may also be\nprone to bugs if due to the code changes, the values that have been considered overwritten actually\nappear to be alive. It is a good practice to keep variable lifetimes as short as possible, and not\nto reuse local variables for the sake of brevity.\n\nExample:\n\n\n void x() {\n String s = \"one\";\n System.out.println(\"s = \" + s);\n s = \"two\"; //reuse of local variable 's'\n System.out.println(\"s = \" + s);\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReuseOfLocalVariable", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Data flow", + "index": 23, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BooleanMethodNameMustStartWithQuestion", + "shortDescription": { + "text": "Boolean method name must start with question word" + }, + "fullDescription": { + "text": "Reports boolean methods whose names do not start with a question word. Boolean methods that override library methods are ignored by this inspection. Example: 'boolean empty(List list) {\n return list.isEmpty();\n}' A quick-fix that renames such methods is available only in the editor. Configure the inspection: Use the Boolean method name prefixes list to specify acceptable question words to start boolean method names with. Use the Ignore methods with 'java.lang.Boolean' return type option to ignore methods with the 'java.lang.Boolean' return type. Use the Ignore boolean methods in an @interface option to ignore boolean methods in annotation types ('@interface'). Use the Ignore methods overriding/implementing a super method to ignore methods the have supers.", + "markdown": "Reports boolean methods whose names do not start with a question word.\n\nBoolean methods that override library methods are ignored by this inspection.\n\n**Example:**\n\n boolean empty(List list) {\n return list.isEmpty();\n }\n\nA quick-fix that renames such methods is available only in the editor.\n\nConfigure the inspection:\n\n* Use the **Boolean method name prefixes** list to specify acceptable question words to start boolean method names with.\n* Use the **Ignore methods with 'java.lang.Boolean' return type** option to ignore methods with the `java.lang.Boolean` return type.\n* Use the **Ignore boolean methods in an @interface** option to ignore boolean methods in annotation types (`@interface`).\n* Use the **Ignore methods overriding/implementing a super method** to ignore methods the have supers." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "BooleanMethodNameMustStartWithQuestion", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions/Method", + "index": 88, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspiciousToArrayCall", + "shortDescription": { + "text": "Suspicious 'Collection.toArray()' call" + }, + "fullDescription": { + "text": "Reports suspicious calls to 'Collection.toArray()'. The following types of calls are considered suspicious: when the type of the array argument is not the same as the array type to which the result is casted. when the type of the array argument does not match the type parameter in the collection declaration. Example: 'void m1(List list) {\n Number[] ns = (Number[]) list.toArray(new String[0]);\n}\n\nvoid m2(List list) {\n Number[] ns = list.toArray(new String[0]);\n}'", + "markdown": "Reports suspicious calls to `Collection.toArray()`.\n\nThe following types of calls are considered suspicious:\n\n* when the type of the array argument is not the same as the array type to which the result is casted.\n* when the type of the array argument does not match the type parameter in the collection declaration.\n\n**Example:**\n\n\n void m1(List list) {\n Number[] ns = (Number[]) list.toArray(new String[0]);\n }\n\n void m2(List list) {\n Number[] ns = list.toArray(new String[0]);\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SuspiciousToArrayCall", + "cweIds": [ + 704 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringToUpperWithoutLocale", + "shortDescription": { + "text": "Call to 'String.toUpperCase()' or 'toLowerCase()' without locale" + }, + "fullDescription": { + "text": "Reports 'toUpperCase()' or 'toLowerCase()' calls on 'String' objects that do not specify a 'java.util.Locale'. In these cases the default system locale is used, which can cause problems in an internationalized environment. For example the code '\"i\".toUpperCase().equals(\"I\")' returns 'false' in the Turkish and Azerbaijani locales, where the dotted and dotless 'i' are separate letters. Calling 'toUpperCase()' on an English string containing an 'i', when running in a Turkish locale, will return incorrect results. Alternatively, when dealing with strings that should be treated as locale-independent, like HTML tags, this can lead to errors.", + "markdown": "Reports `toUpperCase()` or `toLowerCase()` calls on `String` objects that do not specify a `java.util.Locale`. In these cases the default system locale is used, which can cause problems in an internationalized environment.\n\n\nFor example the code `\"i\".toUpperCase().equals(\"I\")` returns `false` in the Turkish and Azerbaijani locales, where\nthe dotted and dotless 'i' are separate letters. Calling `toUpperCase()` on an English string containing an 'i', when running\nin a Turkish locale, will return incorrect results. Alternatively, when dealing with strings that should be treated as locale-independent,\nlike HTML tags, this can lead to errors." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StringToUpperCaseOrToLowerCaseWithoutLocale", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Internationalization", + "index": 9, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SynchronizationOnLocalVariableOrMethodParameter", + "shortDescription": { + "text": "Synchronization on local variable or method parameter" + }, + "fullDescription": { + "text": "Reports synchronization on a local variable or parameter. It is very difficult to guarantee correct operation when such synchronization is used. It may be possible to improve such code, for example, by controlling access using a synchronized wrapper class or by synchronizing on a field. Example: 'void bar() {\n final Object lock = new Object();\n synchronized (lock) { }\n }'", + "markdown": "Reports synchronization on a local variable or parameter.\n\n\nIt is very difficult to guarantee correct operation when such synchronization is used.\nIt may be possible to improve such code, for example, by controlling access using a synchronized wrapper class or by synchronizing on a\nfield.\n\n**Example:**\n\n\n void bar() {\n final Object lock = new Object();\n synchronized (lock) { }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SynchronizationOnLocalVariableOrMethodParameter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NestedMethodCall", + "shortDescription": { + "text": "Nested method call" + }, + "fullDescription": { + "text": "Reports method calls used as parameters to another method call. The quick-fix introduces a variable to make the code simpler and easier to debug. Example: 'public int y() { return 1; }\n public int f(int x) { return 2 * x; }\n\n public void foo() {\n int x = f(y());\n }' After the quick-fix is applied: 'public int y() { return 1; }\n public int f(int x) { return 2 * x; }\n\n public void foo() {\n int y = y();\n int x = f(y);\n }' Use the inspection options to toggle the reporting of: method calls in field initializers calls to static methods calls to simple getters", + "markdown": "Reports method calls used as parameters to another method call.\n\nThe quick-fix introduces a variable to make the code simpler and easier to debug.\n\n**Example:**\n\n\n public int y() { return 1; }\n public int f(int x) { return 2 * x; }\n\n public void foo() {\n int x = f(y());\n }\n\nAfter the quick-fix is applied:\n\n\n public int y() { return 1; }\n public int f(int x) { return 2 * x; }\n\n public void foo() {\n int y = y();\n int x = f(y);\n }\n\n\nUse the inspection options to toggle the reporting of:\n\n* method calls in field initializers\n* calls to static methods\n* calls to simple getters" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NestedMethodCall", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FinalMethod", + "shortDescription": { + "text": "Method can't be overridden" + }, + "fullDescription": { + "text": "Reports methods that are declared 'final'. Such methods can't be overridden and may indicate a lack of object-oriented design. Some coding standards discourage 'final' methods.", + "markdown": "Reports methods that are declared `final`. Such methods can't be overridden and may indicate a lack of object-oriented design. Some coding standards discourage `final` methods." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "FinalMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NegatedConditionalExpression", + "shortDescription": { + "text": "Negated conditional expression" + }, + "fullDescription": { + "text": "Reports conditional expressions which are negated with a prefix expression, as such constructions may be confusing. There is a fix that propagates the outer negation to both branches. Example: '!(i == 1 ? a : b)' After the quick-fix is applied: 'i == 1 ? !a : !b'", + "markdown": "Reports conditional expressions which are negated with a prefix expression, as such constructions may be confusing.\n\nThere is a fix that propagates the outer negation to both branches.\n\nExample:\n\n\n !(i == 1 ? a : b)\n\nAfter the quick-fix is applied:\n\n\n i == 1 ? !a : !b\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NegatedConditionalExpression", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassWithTooManyTransitiveDependents", + "shortDescription": { + "text": "Class with too many transitive dependents" + }, + "fullDescription": { + "text": "Reports a class on which too many other classes are directly or indirectly dependent. Any modification to such a class may require changing many other classes, which may be expensive. Only top-level classes are reported. Use the Maximum number of transitive dependents field to specify the maximum allowed number of direct or indirect dependents for a class. Available only from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name and isn't reported in the editor.", + "markdown": "Reports a class on which too many other classes are directly or indirectly dependent.\n\nAny modification to such a class may require changing many other classes, which may be expensive.\n\nOnly top-level classes are reported.\n\nUse the **Maximum number of transitive dependents** field to specify the maximum allowed number of direct or indirect dependents\nfor a class.\n\nAvailable only from **Code \\| Inspect Code** or\n**Code \\| Analyze Code \\| Run Inspection by Name** and isn't reported in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassWithTooManyTransitiveDependents", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Dependency issues", + "index": 89, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CompareToUsesNonFinalVariable", + "shortDescription": { + "text": "Non-final field referenced in 'compareTo()'" + }, + "fullDescription": { + "text": "Reports access to a non-'final' field inside a 'compareTo()' implementation. Such access may result in 'compareTo()' returning different results at different points in the object's lifecycle, which may in turn cause problems when using the standard collections classes, for example 'java.util.TreeSet'. A quick-fix to make the field 'final' is available only when there is no write access to the field, otherwise no fixes are suggested. Example: 'class Foo implements Comparable{\n private int index;\n Foo(int idx) {\n index = idx;\n }\n @Override\n public int compareTo(Foo foo) {\n return Integer.compare(this.index, foo.index);\n }\n }' After the quick-fix is applied: 'class Foo implements Comparable{\n private final int index;\n Foo(int idx) {\n index = idx;\n }\n @Override\n public int compareTo(Foo foo) {\n return Integer.compare(this.index, foo.index);\n }\n }'", + "markdown": "Reports access to a non-`final` field inside a `compareTo()` implementation.\n\n\nSuch access may result in `compareTo()`\nreturning different results at different points in the object's lifecycle, which may in turn cause problems when\nusing the standard collections classes, for example `java.util.TreeSet`.\n\n\nA quick-fix to make the field `final` is available\nonly when there is no write access to the field, otherwise no fixes are suggested.\n\n**Example:**\n\n\n class Foo implements Comparable{\n private int index;\n Foo(int idx) {\n index = idx;\n }\n @Override\n public int compareTo(Foo foo) {\n return Integer.compare(this.index, foo.index);\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo implements Comparable{\n private final int index;\n Foo(int idx) {\n index = idx;\n }\n @Override\n public int compareTo(Foo foo) {\n return Integer.compare(this.index, foo.index);\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CompareToUsesNonFinalVariable", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JavacQuirks", + "shortDescription": { + "text": "Javac quirks" + }, + "fullDescription": { + "text": "Reports known Javac issues, performance problems, and incompatibilities. For example, type inference may be slow when it has to process many nested calls. The following code triggers a warning, as the vararg method call has 50+ poly arguments: 'Arrays.asList(\n Arrays.asList(\"a1\", \"b1\"),\n Arrays.asList(\"a2\", \"b2\"),\n ...\n Arrays.asList(\"a100\", \"b100\"));' The quick-fix adds explicit type arguments, which makes compilation and IDE processing much faster: '//noinspection RedundantTypeArguments\n Arrays.>asList(\n Arrays.asList(\"a1\", \"b1\"),\n Arrays.asList(\"a2\", \"b2\"),\n ...\n Arrays.asList(\"a100\", \"b100\"));'", + "markdown": "Reports known Javac issues, performance problems, and incompatibilities. For example, type inference may be slow when it has to process many nested calls.\n\nThe following code triggers a warning, as the vararg method call has 50+ poly arguments:\n\n\n Arrays.asList(\n Arrays.asList(\"a1\", \"b1\"),\n Arrays.asList(\"a2\", \"b2\"),\n ...\n Arrays.asList(\"a100\", \"b100\"));\n\nThe quick-fix adds explicit type arguments, which makes compilation and IDE processing much faster:\n\n\n //noinspection RedundantTypeArguments\n Arrays.>asList(\n Arrays.asList(\"a1\", \"b1\"),\n Arrays.asList(\"a2\", \"b2\"),\n ...\n Arrays.asList(\"a100\", \"b100\"));\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "JavacQuirks", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Compiler issues", + "index": 90, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspiciousSystemArraycopy", + "shortDescription": { + "text": "Suspicious 'System.arraycopy()' call" + }, + "fullDescription": { + "text": "Reports suspicious calls to 'System.arraycopy()'. Such calls are suspicious when: the source or destination is not of an array type the source and destination are of different types the copied chunk length is greater than 'src.length - srcPos' the copied chunk length is greater than 'dest.length - destPos' the ranges always intersect when the source and destination are the same array Example: 'void foo() {\n int[] src = new int[] { 1, 2, 3, 4 };\n System.arraycopy(src, 0, src, 1, 2); // warning: Copying to the same array with intersecting ranges\n }'", + "markdown": "Reports suspicious calls to `System.arraycopy()`.\n\nSuch calls are suspicious when:\n\n* the source or destination is not of an array type\n* the source and destination are of different types\n* the copied chunk length is greater than `src.length - srcPos`\n* the copied chunk length is greater than `dest.length - destPos`\n* the ranges always intersect when the source and destination are the same array\n\n**Example:**\n\n\n void foo() {\n int[] src = new int[] { 1, 2, 3, 4 };\n System.arraycopy(src, 0, src, 1, 2); // warning: Copying to the same array with intersecting ranges\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SuspiciousSystemArraycopy", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SwitchStatement", + "shortDescription": { + "text": "'switch' statement" + }, + "fullDescription": { + "text": "Reports 'switch' statements. 'switch' statements often (but not always) indicate a poor object-oriented design. Example: 'switch (i) {\n // code\n }'", + "markdown": "Reports `switch` statements.\n\n`switch` statements often (but not always) indicate a poor object-oriented design.\n\nExample:\n\n\n switch (i) {\n // code\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SwitchStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AbsoluteAlignmentInUserInterface", + "shortDescription": { + "text": "Absolute alignment in AWT/Swing code" + }, + "fullDescription": { + "text": "Reports usages of absolute alignment constants from AWT and Swing. Internationalized applications use relative alignment because it respects the locale component orientation settings. Example: 'JPanel panel = new JPanel(new BorderLayout(2, 2));\n JLabel label = new JLabel(\"Hello World\");\n panel.add(label, BorderLayout.NORTH);' After the quick-fix is applied: 'JPanel panel = new JPanel(new BorderLayout(2, 2));\n JLabel label = new JLabel(\"Hello World\");\n panel.add(label, BorderLayout.PAGE_START);'", + "markdown": "Reports usages of absolute alignment constants from AWT and Swing. Internationalized applications use relative alignment because it respects the locale component orientation settings.\n\n**Example:**\n\n\n JPanel panel = new JPanel(new BorderLayout(2, 2));\n JLabel label = new JLabel(\"Hello World\");\n panel.add(label, BorderLayout.NORTH);\n\nAfter the quick-fix is applied:\n\n\n JPanel panel = new JPanel(new BorderLayout(2, 2));\n JLabel label = new JLabel(\"Hello World\");\n panel.add(label, BorderLayout.PAGE_START);\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AbsoluteAlignmentInUserInterface", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Internationalization", + "index": 9, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringBufferReplaceableByString", + "shortDescription": { + "text": "'StringBuilder' can be replaced with 'String'" + }, + "fullDescription": { + "text": "Reports usages of 'StringBuffer', 'StringBuilder', or 'StringJoiner' which can be replaced with a single 'String' concatenation. Using 'String' concatenation makes the code shorter and simpler. This inspection only reports when the suggested replacement does not result in significant performance drawback on modern JVMs. In many cases, 'String' concatenation may perform better. Example: 'StringBuilder result = new StringBuilder();\n result.append(\"i = \");\n result.append(i);\n result.append(\";\");\n return result.toString();' After the quick-fix is applied: 'String result = \"i = \" + i + \";\";\n return result;'", + "markdown": "Reports usages of `StringBuffer`, `StringBuilder`, or `StringJoiner` which can be replaced with a single `String` concatenation.\n\nUsing `String` concatenation\nmakes the code shorter and simpler.\n\n\nThis inspection only reports when the suggested replacement does not result in significant\nperformance drawback on modern JVMs. In many cases, `String` concatenation may perform better.\n\n**Example:**\n\n\n StringBuilder result = new StringBuilder();\n result.append(\"i = \");\n result.append(i);\n result.append(\";\");\n return result.toString();\n\nAfter the quick-fix is applied:\n\n\n String result = \"i = \" + i + \";\";\n return result;\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StringBufferReplaceableByString", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SourceToSinkFlow", + "shortDescription": { + "text": "Non-safe string is passed to safe method" + }, + "fullDescription": { + "text": "Reports cases when a non-safe object is passed to a method with a parameter marked with '@Untainted' annotations, returned from annotated methods or assigned to annotated fields, parameters, or local variables. Kotlin 'set' and 'get' methods for fields are not supported as entry points. A safe object (in the same class) is: a string literal, interface instance, or enum object a result of a call of a method that is marked as '@Untainted' a private field, which is assigned only with a string literal and has a safe initializer a final field, which has a safe initializer local variable or parameter that are marked as '@Untainted' and are not assigned from non-safe objects This field, local variable, or parameter must not be passed as arguments to methods or used as a qualifier or must be a primitive, its wrapper or immutable. Also static final fields are considered as safe. The analysis is performed only inside one file. To process dependencies from other classes, use options. The analysis extends to private or static methods and has a limit of depth propagation. Example: 'void doSmth(boolean b) {\n String s = safe();\n String s1 = \"other\";\n if (b) s1 = s;\n sink(s);\n }\n\n String sink(@Untainted String s) {}'\n Here we do not have non-safe string assignments to 's' so a warning is not produced. On the other hand: 'void doSmth(boolean b) {\n String s = safe();\n String s1 = \"other\";\n s1 = foo();\n if (b) s = s1;\n sink(s); // warning here\n }\n\n String foo();\n\n String sink(@Untainted String s) {}'\n Here we have a warning since 's1' has an unknown state after 'foo' call result assignment. New in 2021.2", + "markdown": "Reports cases when a non-safe object is passed to a method with a parameter marked with `@Untainted` annotations, returned from annotated methods or assigned to annotated fields, parameters, or local variables. Kotlin `set` and `get` methods for fields are not supported as entry points.\n\n\nA safe object (in the same class) is:\n\n* a string literal, interface instance, or enum object\n* a result of a call of a method that is marked as `@Untainted`\n* a private field, which is assigned only with a string literal and has a safe initializer\n* a final field, which has a safe initializer\n* local variable or parameter that are marked as `@Untainted` and are not assigned from non-safe objects\nThis field, local variable, or parameter must not be passed as arguments to methods or used as a qualifier or must be a primitive, its wrapper or immutable.\n\nAlso static final fields are considered as safe.\n\n\nThe analysis is performed only inside one file. To process dependencies from other classes, use options.\nThe analysis extends to private or static methods and has a limit of depth propagation.\n\n\nExample:\n\n\n void doSmth(boolean b) {\n String s = safe();\n String s1 = \"other\";\n if (b) s1 = s;\n sink(s);\n }\n\n String sink(@Untainted String s) {}\n\n\nHere we do not have non-safe string assignments to `s` so a warning is not produced. On the other hand:\n\n\n void doSmth(boolean b) {\n String s = safe();\n String s1 = \"other\";\n s1 = foo();\n if (b) s = s1;\n sink(s); // warning here\n }\n\n String foo();\n\n String sink(@Untainted String s) {}\n\n\nHere we have a warning since `s1` has an unknown state after `foo` call result assignment.\n\nNew in 2021.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "tainting", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages", + "index": 2, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RecordCanBeClass", + "shortDescription": { + "text": "Record can be converted to class" + }, + "fullDescription": { + "text": "Reports record classes and suggests converting them to ordinary classes. This inspection makes it possible to move a Java record to a codebase using an earlier Java version by applying the quick-fix to this record. Note that the resulting class is not completely equivalent to the original record: The resulting class no longer extends 'java.lang.Record', so 'instanceof Record' returns 'false'. Reflection methods like 'Class.isRecord()' and 'Class.getRecordComponents()' produce different results. The generated 'hashCode()' implementation may produce a different result because the formula to calculate record 'hashCode' is deliberately not specified. Record serialization mechanism differs from that of an ordinary class. Refer to Java Object Serialization Specification for details. Example: 'record Point(int x, int y) {}' After the quick-fix is applied: 'final class Point {\n private final int x;\n private final int y;\n\n Point(int x, int y) {\n this.x = x;\n this.y = y;\n }\n\n public int x() { return x; }\n\n public int y() { return y; }\n\n @Override\n public boolean equals(Object obj) {\n if (obj == this) return true;\n if (obj == null || obj.getClass() != this.getClass()) return false;\n var that = (Point)obj;\n return this.x == that.x &&\n this.y == that.y;\n }\n\n @Override\n public int hashCode() {\n return Objects.hash(x, y);\n }\n\n @Override\n public String toString() {\n return \"Point[\" +\n \"x=\" + x + \", \" +\n \"y=\" + y + ']';\n }\n }' This inspection only reports if the language level of the project or module is 16 or higher. New in 2020.3", + "markdown": "Reports record classes and suggests converting them to ordinary classes.\n\nThis inspection makes it possible to move a Java record to a codebase using an earlier Java version\nby applying the quick-fix to this record.\n\n\nNote that the resulting class is not completely equivalent to the original record:\n\n* The resulting class no longer extends `java.lang.Record`, so `instanceof Record` returns `false`.\n* Reflection methods like `Class.isRecord()` and `Class.getRecordComponents()` produce different results.\n* The generated `hashCode()` implementation may produce a different result because the formula to calculate record `hashCode` is deliberately not specified.\n* Record serialization mechanism differs from that of an ordinary class. Refer to *Java Object Serialization Specification* for details.\n\nExample:\n\n\n record Point(int x, int y) {}\n\nAfter the quick-fix is applied:\n\n\n final class Point {\n private final int x;\n private final int y;\n\n Point(int x, int y) {\n this.x = x;\n this.y = y;\n }\n\n public int x() { return x; }\n\n public int y() { return y; }\n\n @Override\n public boolean equals(Object obj) {\n if (obj == this) return true;\n if (obj == null || obj.getClass() != this.getClass()) return false;\n var that = (Point)obj;\n return this.x == that.x &&\n this.y == that.y;\n }\n\n @Override\n public int hashCode() {\n return Objects.hash(x, y);\n }\n\n @Override\n public String toString() {\n return \"Point[\" +\n \"x=\" + x + \", \" +\n \"y=\" + y + ']';\n }\n }\n\nThis inspection only reports if the language level of the project or module is 16 or higher.\n\nNew in 2020.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RecordCanBeClass", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantLambdaParameterType", + "shortDescription": { + "text": "Redundant lambda parameter types" + }, + "fullDescription": { + "text": "Reports lambda formal parameter types that are redundant because they can be inferred from the context. Example: 'Map map = ...\n map.forEach((String s, Integer i) -> log.info(s + \"=\" + i));' The quick-fix removes the parameter types from the lambda. 'Map map = ...\n map.forEach((s, i) -> log.info(s + \"=\" + i));'", + "markdown": "Reports lambda formal parameter types that are redundant because they can be inferred from the context.\n\n**Example:**\n\n\n Map map = ...\n map.forEach((String s, Integer i) -> log.info(s + \"=\" + i));\n\nThe quick-fix removes the parameter types from the lambda.\n\n\n Map map = ...\n map.forEach((s, i) -> log.info(s + \"=\" + i));\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RedundantLambdaParameterType", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MalformedFormatString", + "shortDescription": { + "text": "Malformed format string" + }, + "fullDescription": { + "text": "Reports format strings that don't comply with the standard Java syntax. By default, the inspection considers a compile-time constant a format string if it's used as an argument to the corresponding methods on 'java.util.Formatter', 'java.lang.String', 'java.io.PrintWriter' or 'java.io.PrintStream'. Example: 'String.format(\"x = %d, y = %d\", 42);' Use the inspection settings to mark additional classes and methods as related to string formatting. As an alternative, you can use the 'org.intellij.lang.annotations.PrintFormat' annotation to mark the format string method parameter. In this case, the format arguments parameter must immediately follow the format string and be the last method parameter. Example: 'void myFormatMethod(int mode, @PrintFormat String formatString, Object... args) {...}' Methods annotated in this way will also be recognized by this inspection.", + "markdown": "Reports format strings that don't comply with the standard Java syntax.\n\nBy default, the inspection considers a compile-time constant a format string if it's used as an argument to the corresponding methods on\n`java.util.Formatter`, `java.lang.String`, `java.io.PrintWriter` or `java.io.PrintStream`.\n\n**Example:**\n\n\n String.format(\"x = %d, y = %d\", 42);\n\nUse the inspection settings to mark additional classes and methods as related to string formatting.\n\nAs an alternative, you can use the `org.intellij.lang.annotations.PrintFormat` annotation\nto mark the format string method parameter. In this case,\nthe format arguments parameter must immediately follow the format string and be the last method parameter. Example:\n\n\n void myFormatMethod(int mode, @PrintFormat String formatString, Object... args) {...}\n\n\nMethods annotated in this way will also be recognized by this inspection." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "MalformedFormatString", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConditionalExpression", + "shortDescription": { + "text": "Conditional expression" + }, + "fullDescription": { + "text": "Reports usages of the ternary condition operator and suggests converting them to 'if'/'else' statements. Some code standards prohibit the use of the condition operator. Example: 'Object result = (condition) ? foo() : bar();' After the quick-fix is applied: 'Object result;\n if (condition) {\n comp = foo();\n }\n else {\n comp = bar();\n }' Configure the inspection: Use the Ignore for simple assignments and returns option to ignore simple assignments and returns and allow the following constructs: 'String s = (foo == null) ? \"\" : foo.toString();' Use the Ignore places where an if statement is not possible option to ignore conditional expressions in contexts in which automatic replacement with an if statement is not possible (for example, when the conditional expression is used as an argument to a 'super()' constructor call).", + "markdown": "Reports usages of the ternary condition operator and suggests converting them to `if`/`else` statements.\n\nSome code standards prohibit the use of the condition operator.\n\nExample:\n\n\n Object result = (condition) ? foo() : bar();\n\nAfter the quick-fix is applied:\n\n\n Object result;\n if (condition) {\n comp = foo();\n }\n else {\n comp = bar();\n }\n\nConfigure the inspection:\n\nUse the **Ignore for simple assignments and returns** option to ignore simple assignments and returns and allow the following constructs:\n\n\n String s = (foo == null) ? \"\" : foo.toString();\n\n\nUse the **Ignore places where an if statement is not possible** option to ignore conditional expressions in contexts in which automatic\nreplacement with an if statement is not possible (for example, when the conditional expression is used as an argument to a\n`super()` constructor call)." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ConditionalExpression", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspiciousReturnByteInputStream", + "shortDescription": { + "text": "Suspicious byte value returned from 'InputStream.read()'" + }, + "fullDescription": { + "text": "Reports expressions of 'byte' type returned from a method implementing the 'InputStream.read()' method. This is suspicious because 'InputStream.read()' should return a value in the range from '0' to '255', while an expression of byte type contains a value from '-128' to '127'. The quick-fix converts the expression into an unsigned 'byte' by applying the bitmask '0xFF'. Example: 'class MyInputStream extends InputStream {\n int pos = 0;\n byte[] data;\n\n MyInputStream(byte[] input) {\n data = input;\n }\n\n @Override\n public int read() {\n if (pos == data.length) {\n return -1;\n }\n return data[pos++]; // problem\n }\n}' After applying the quick-fix: 'class MyInputStream extends InputStream {\n int pos = 0;\n byte[] data;\n\n MyInputStream(byte[] input) {\n data = input;\n }\n\n @Override\n public int read() {\n if (pos == data.length) {\n return -1;\n }\n return data[pos++] & 0xFF;\n }\n}' New in 2023.2", + "markdown": "Reports expressions of `byte` type returned from a method implementing the `InputStream.read()` method.\n\n\nThis is suspicious because `InputStream.read()` should return a value in the range from `0` to `255`,\nwhile an expression of byte type contains a value from `-128` to `127`.\nThe quick-fix converts the expression into an unsigned `byte` by applying the bitmask `0xFF`.\n\n**Example:**\n\n\n class MyInputStream extends InputStream {\n int pos = 0;\n byte[] data;\n\n MyInputStream(byte[] input) {\n data = input;\n }\n\n @Override\n public int read() {\n if (pos == data.length) {\n return -1;\n }\n return data[pos++]; // problem\n }\n }\n\nAfter applying the quick-fix:\n\n\n class MyInputStream extends InputStream {\n int pos = 0;\n byte[] data;\n\n MyInputStream(byte[] input) {\n data = input;\n }\n\n @Override\n public int read() {\n if (pos == data.length) {\n return -1;\n }\n return data[pos++] & 0xFF;\n }\n }\n\nNew in 2023.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SuspiciousReturnByteInputStream", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ThreadYield", + "shortDescription": { + "text": "Call to 'Thread.yield()'" + }, + "fullDescription": { + "text": "Reports calls to 'Thread.yield()'. The behavior of 'yield()' is non-deterministic and platform-dependent, and it is rarely appropriate to use this method. Its use should be combined with detailed profiling and benchmarking to ensure that it actually has the desired effect. Example: 'public static void main(String[] args) {\n Runnable r = () -> {\n for (int i = 0; i < 10; i++) {\n System.out.println(i);\n Thread.yield();\n }\n };\n new Thread(r).start();\n new Thread(r).start();\n }'", + "markdown": "Reports calls to `Thread.yield()`.\n\n\nThe behavior of `yield()` is non-deterministic and platform-dependent, and it is rarely appropriate to use this method.\nIts use should be combined with detailed profiling and benchmarking to ensure that it actually has the desired effect.\n\n**Example:**\n\n\n public static void main(String[] args) {\n Runnable r = () -> {\n for (int i = 0; i < 10; i++) {\n System.out.println(i);\n Thread.yield();\n }\n };\n new Thread(r).start();\n new Thread(r).start();\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToThreadYield", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UseOfClone", + "shortDescription": { + "text": "Use of 'clone()' or 'Cloneable'" + }, + "fullDescription": { + "text": "Reports implementations of, and calls to, the 'clone()' method and uses of the 'java.lang.Cloneable' interface. Some coding standards prohibit the use of 'clone()', and recommend using a copy constructor or a 'static' factory method instead. The inspection ignores calls to 'clone()' on arrays because it's a correct and compact way to copy an array. Example: 'class Copy implements Cloneable /*warning*/ {\n\n public Copy clone() /*warning*/ {\n try {\n return (Copy) super.clone(); // warning\n } catch (CloneNotSupportedException e) {\n throw new AssertionError();\n }\n }\n }'", + "markdown": "Reports implementations of, and calls to, the `clone()` method and uses of the `java.lang.Cloneable` interface.\n\nSome coding standards prohibit the use of `clone()`, and recommend using a copy constructor or\na `static` factory method instead.\n\nThe inspection ignores calls to `clone()` on arrays because it's a correct and compact way to copy an array.\n\n**Example:**\n\n\n class Copy implements Cloneable /*warning*/ {\n\n public Copy clone() /*warning*/ {\n try {\n return (Copy) super.clone(); // warning\n } catch (CloneNotSupportedException e) {\n throw new AssertionError();\n }\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UseOfClone", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Cloning issues", + "index": 82, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MissingFinalNewline", + "shortDescription": { + "text": "Missing final new line" + }, + "fullDescription": { + "text": "Reports if manifest files do not end with a final newline as required by the JAR file specification.", + "markdown": "Reports if manifest files do not end with a final newline as required by the JAR file specification." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "MissingFinalNewline", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Manifest", + "index": 93, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LambdaCanBeMethodCall", + "shortDescription": { + "text": "Lambda can be replaced with method call" + }, + "fullDescription": { + "text": "Reports lambda expressions which can be replaced with a call to a JDK method. For example, an expression 'x -> x' of type 'Function' can be replaced with a 'Function.identity()' call. New in 2017.1", + "markdown": "Reports lambda expressions which can be replaced with a call to a JDK method.\n\nFor example, an expression `x -> x` of type `Function`\ncan be replaced with a `Function.identity()` call.\n\nNew in 2017.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "LambdaCanBeMethodCall", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 8", + "index": 62, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NestedTryStatement", + "shortDescription": { + "text": "Nested 'try' statement" + }, + "fullDescription": { + "text": "Reports nested 'try' statements. Nested 'try' statements may result in unclear code and should probably have their 'catch' and 'finally' sections merged.", + "markdown": "Reports nested `try` statements.\n\nNested `try` statements\nmay result in unclear code and should probably have their `catch` and `finally` sections\nmerged." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NestedTryStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EndlessStream", + "shortDescription": { + "text": "Non-short-circuit operation consumes infinite stream" + }, + "fullDescription": { + "text": "Reports non-short-circuit operations consuming an infinite stream. Such operations can be completed only by throwing an exception. Example: 'Stream.iterate(0, i -> i + 1).collect(Collectors.toList())'", + "markdown": "Reports non-short-circuit operations consuming an infinite stream. Such operations can be completed only by throwing an exception.\n\nExample:\n\n\n Stream.iterate(0, i -> i + 1).collect(Collectors.toList())\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "EndlessStream", + "cweIds": [ + 835 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Singleton", + "shortDescription": { + "text": "Singleton" + }, + "fullDescription": { + "text": "Reports singleton classes. Singleton classes are declared in a way that only one instance of the class can ever be instantiated. Singleton classes complicate testing, and their presence may indicate a lack of object-oriented design. Example: 'class Singleton {\n private static final Singleton ourInstance = new Singleton();\n\n private Singleton() {\n }\n\n public Singleton getInstance() {\n return ourInstance;\n }\n }'", + "markdown": "Reports singleton classes.\n\nSingleton classes are declared in a way that only one instance of the class can ever be instantiated. Singleton classes complicate testing,\nand their presence may indicate a lack of object-oriented design.\n\n**Example:**\n\n\n class Singleton {\n private static final Singleton ourInstance = new Singleton();\n\n private Singleton() {\n }\n\n public Singleton getInstance() {\n return ourInstance;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "Singleton", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonStaticFinalLogger", + "shortDescription": { + "text": "Non-constant logger" + }, + "fullDescription": { + "text": "Reports logger fields that are not declared 'static' and/or 'final'. Ensuring that every class logger is effectively constant and bound to that class simplifies the task of providing a unified logging implementation for an application. A quick-fix is provided to change the logger modifiers to 'static final'. Example: 'public class Significant {\n private Logger LOG = Logger.getLogger(Critical.class);\n }' After the quick-fix is applied: 'public class Significant {\n private static final Logger LOG = Logger.getLogger(Critical.class);\n }' Configure the inspection: Use the Logger class name table to specify logger class names. The inspection will report the fields that are not 'static' and 'final' and are of the type equal to one of the specified class names.", + "markdown": "Reports logger fields that are not declared `static` and/or `final`. Ensuring that every class logger is effectively constant and bound to that class simplifies the task of providing a unified logging implementation for an application.\n\nA quick-fix is provided to change the logger modifiers to `static final`.\n\n**Example:**\n\n\n public class Significant {\n private Logger LOG = Logger.getLogger(Critical.class);\n }\n\nAfter the quick-fix is applied:\n\n\n public class Significant {\n private static final Logger LOG = Logger.getLogger(Critical.class);\n }\n\n\nConfigure the inspection:\n\n* Use the **Logger class name** table to specify logger class names. The inspection will report the fields that are not `static` and `final` and are of the type equal to one of the specified class names." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonConstantLogger", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Logging", + "index": 68, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonFinalUtilityClass", + "shortDescription": { + "text": "Utility class is not 'final'" + }, + "fullDescription": { + "text": "Reports utility classes that aren't 'final' or 'abstract'. Utility classes have all fields and methods declared as 'static'. Making them 'final' prevents them from being accidentally subclassed. Example: 'public class UtilityClass {\n public static void foo() {}\n }' After the quick-fix is applied: 'public final class UtilityClass {\n public static void foo() {}\n }'", + "markdown": "Reports utility classes that aren't `final` or `abstract`.\n\nUtility classes have all fields and methods declared as `static`.\nMaking them `final` prevents them from being accidentally subclassed.\n\n**Example:**\n\n\n public class UtilityClass {\n public static void foo() {}\n }\n\nAfter the quick-fix is applied:\n\n\n public final class UtilityClass {\n public static void foo() {}\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonFinalUtilityClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FuseStreamOperations", + "shortDescription": { + "text": "Subsequent steps can be fused into Stream API chain" + }, + "fullDescription": { + "text": "Detects transformations outside a Stream API chain that could be incorporated into it. Example: 'List list = stream.collect(Collectors.toList());\n list.sort(null);\n return list.toArray(new String[list.size()]);' After the conversion: 'return stream.sorted().toArray(String[]::new);' Note that sometimes the converted stream chain may replace explicit 'ArrayList' with 'Collectors.toList()' or explicit 'HashSet' with 'Collectors.toSet()'. The current library implementation uses these collections internally. However, this approach is not very reliable and might change in the future altering the semantics of your code. If you are concerned about it, use the Do not suggest 'toList()' or 'toSet()' collectors option to suggest 'Collectors.toCollection()' instead of 'toList' and 'toSet' collectors. This inspection only reports if the language level of the project or module is 8 or higher.", + "markdown": "Detects transformations outside a Stream API chain that could be incorporated into it.\n\nExample:\n\n\n List list = stream.collect(Collectors.toList());\n list.sort(null);\n return list.toArray(new String[list.size()]);\n\nAfter the conversion:\n\n\n return stream.sorted().toArray(String[]::new);\n\n\nNote that sometimes the converted stream chain may replace explicit `ArrayList` with `Collectors.toList()` or explicit\n`HashSet` with `Collectors.toSet()`. The current library implementation uses these collections internally. However,\nthis approach is not very reliable and might change in the future altering the semantics of your code.\n\nIf you are concerned about it, use the **Do not suggest 'toList()' or 'toSet()' collectors** option to suggest\n`Collectors.toCollection()` instead of `toList` and `toSet` collectors.\n\nThis inspection only reports if the language level of the project or module is 8 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "FuseStreamOperations", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConditionalExpressionWithIdenticalBranches", + "shortDescription": { + "text": "Conditional expression with identical branches" + }, + "fullDescription": { + "text": "Reports conditional expressions with identical 'then' and 'else' branches. Such expressions almost certainly indicate bugs. The inspection provides a fix that collapses conditional expressions. Example: 'int y = x == 10 ? 4 : 4;' After the quick-fix is applied: 'int y = 4;'", + "markdown": "Reports conditional expressions with identical `then` and `else` branches.\n\nSuch expressions almost certainly indicate bugs. The inspection provides a fix that collapses conditional expressions.\n\nExample:\n\n\n int y = x == 10 ? 4 : 4;\n\nAfter the quick-fix is applied:\n\n\n int y = 4;\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ConditionalExpressionWithIdenticalBranches", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DefaultNotLastCaseInSwitch", + "shortDescription": { + "text": "'default' not last case in 'switch'" + }, + "fullDescription": { + "text": "Reports 'switch' statements or expressions in which the 'default' branch is positioned before another case. Such a construct is unnecessarily confusing. A quick-fix is provided to move the 'default' branch to the last position, if possible. Example: 'switch (n) {\n default:\n System.out.println();\n break;\n case 1:\n break;\n }' After the quick-fix is applied: 'switch (n) {\n case 1:\n break;\n default:\n System.out.println();\n break;\n }'", + "markdown": "Reports `switch` statements or expressions in which the `default` branch is positioned before another case. Such a construct is unnecessarily confusing. A quick-fix is provided to move the `default` branch to the last position, if possible.\n\n**Example:**\n\n\n switch (n) {\n default:\n System.out.println();\n break;\n case 1:\n break;\n }\n\nAfter the quick-fix is applied:\n\n\n switch (n) {\n case 1:\n break;\n default:\n System.out.println();\n break;\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "DefaultNotLastCaseInSwitch", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NullThrown", + "shortDescription": { + "text": "'null' thrown" + }, + "fullDescription": { + "text": "Reports 'null' literals that are used as the argument of a 'throw' statement. Such constructs produce a 'java.lang.NullPointerException' that usually should not be thrown programmatically.", + "markdown": "Reports `null` literals that are used as the argument of a `throw` statement.\n\nSuch constructs produce a `java.lang.NullPointerException` that usually should not be thrown programmatically." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NullThrown", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LocalCanBeFinal", + "shortDescription": { + "text": "Local variable or parameter can be 'final'" + }, + "fullDescription": { + "text": "Reports parameters or local variables that may have the 'final' modifier added to their declaration. Example: 'ArrayList list = new ArrayList();\n fill(list);\n return list;' After the quick-fix is applied: 'final ArrayList list = new ArrayList();\n fill(list);\n return list;' Use the inspection's options to define whether parameters or local variables should be reported.", + "markdown": "Reports parameters or local variables that may have the `final` modifier added to their declaration.\n\nExample:\n\n\n ArrayList list = new ArrayList();\n fill(list);\n return list;\n\nAfter the quick-fix is applied:\n\n\n final ArrayList list = new ArrayList();\n fill(list);\n return list;\n\n\nUse the inspection's options to define whether parameters or local variables should be reported." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LocalCanBeFinal", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FieldMayBeStatic", + "shortDescription": { + "text": "Field can be made 'static'" + }, + "fullDescription": { + "text": "Reports instance variables that can safely be made 'static'. A field can be static if it is declared 'final' and initialized with a constant. Example: 'public final String str = \"sample\";' The inspection does not report final fields that can be implicitly written. Use the \"Annotations\" button to modify the list of annotations that assume implicit field write.", + "markdown": "Reports instance variables that can safely be made `static`. A field can be static if it is declared `final` and initialized with a constant.\n\n**Example:**\n\n\n public final String str = \"sample\";\n\n\nThe inspection does not report final fields that can be implicitly written. Use the \"Annotations\" button to modify\nthe list of annotations that assume implicit field write." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "FieldMayBeStatic", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ArrayEquals", + "shortDescription": { + "text": "'equals()' called on array" + }, + "fullDescription": { + "text": "Reports 'equals()' calls that compare two arrays. Calling 'equals()' on an array compares identity and is equivalent to using '=='. Use 'Arrays.equals()' to compare the contents of two arrays, or 'Arrays.deepEquals()' for multi-dimensional arrays. Example: 'void sample(int[] first, int[] second){\n if (first.equals(second)) return;\n }' After the quick-fix is applied: 'void sample(int[] first, int[] second){\n if (Arrays.equals(first, second)) return;\n }'", + "markdown": "Reports `equals()` calls that compare two arrays.\n\nCalling `equals()` on an array compares identity and is equivalent to using `==`.\nUse `Arrays.equals()` to compare the contents of two arrays, or `Arrays.deepEquals()` for\nmulti-dimensional arrays.\n\n**Example:**\n\n\n void sample(int[] first, int[] second){\n if (first.equals(second)) return;\n }\n\nAfter the quick-fix is applied:\n\n\n void sample(int[] first, int[] second){\n if (Arrays.equals(first, second)) return;\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ArrayEquals", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ProblematicVarargsMethodOverride", + "shortDescription": { + "text": "Non-varargs method overrides varargs method" + }, + "fullDescription": { + "text": "Reports methods that override a variable arity (a.k.a. varargs) method but replace the variable arity parameter with an array parameter. Though this code is valid, it may be confusing and should be avoided.", + "markdown": "Reports methods that override a variable arity (a.k.a. varargs) method but replace the variable arity parameter with an array parameter. Though this code is valid, it may be confusing and should be avoided." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ProblematicVarargsMethodOverride", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Inheritance issues", + "index": 25, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OnlyOneElementUsed", + "shortDescription": { + "text": "Only one element is used" + }, + "fullDescription": { + "text": "Reports lists, arrays, and strings where exactly one element is queried right upon the creation. Such expressions may appear after refactoring and usually could be replaced with an accessed element. Example: 'System.out.println(new int[] {1,2,3,4,5}[2]);' After the quick-fix is applied: 'System.out.println(3);' New in 2022.3", + "markdown": "Reports lists, arrays, and strings where exactly one element is queried right upon the creation. Such expressions may appear after refactoring and usually could be replaced with an accessed element.\n\nExample:\n\n\n System.out.println(new int[] {1,2,3,4,5}[2]);\n\nAfter the quick-fix is applied:\n\n\n System.out.println(3);\n\nNew in 2022.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OnlyOneElementUsed", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MisspelledHeader", + "shortDescription": { + "text": "Unknown or misspelled header name" + }, + "fullDescription": { + "text": "Reports any unknown and probably misspelled header names and provides possible variants.", + "markdown": "Reports any unknown and probably misspelled header names and provides possible variants." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "MisspelledHeader", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Manifest", + "index": 93, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AtomicFieldUpdaterIssues", + "shortDescription": { + "text": "Inconsistent 'AtomicFieldUpdater' declaration" + }, + "fullDescription": { + "text": "Reports issues with 'AtomicLongFieldUpdater', 'AtomicIntegerFieldUpdater', or 'AtomicReferenceFieldUpdater' fields (the 'java.util.concurrent.atomic' package). The reported issues are identical to the runtime problems that can happen with atomic field updaters: specified field not found, specified field not accessible, specified field has a wrong type, and so on. Examples: 'class A {\n private static volatile int value = 0;\n private static final AtomicIntegerFieldUpdater updater =\n AtomicIntegerFieldUpdater.newUpdater((A.class), \"value\"); // warning: Field 'value' has 'static' modifier\n }' 'class B {\n private static final AtomicIntegerFieldUpdater updater =\n AtomicIntegerFieldUpdater.newUpdater(B.class, \"value\"); // warning: No field named 'value' found in class 'B'\n }'", + "markdown": "Reports issues with `AtomicLongFieldUpdater`, `AtomicIntegerFieldUpdater`, or `AtomicReferenceFieldUpdater` fields (the `java.util.concurrent.atomic` package).\n\nThe reported issues are identical to the runtime problems that can happen with atomic field updaters:\nspecified field not found, specified field not accessible, specified field has a wrong type, and so on.\n\n**Examples:**\n\n*\n\n\n class A {\n private static volatile int value = 0;\n private static final AtomicIntegerFieldUpdater updater =\n AtomicIntegerFieldUpdater.newUpdater((A.class), \"value\"); // warning: Field 'value' has 'static' modifier\n }\n \n*\n\n\n class B {\n private static final AtomicIntegerFieldUpdater updater =\n AtomicIntegerFieldUpdater.newUpdater(B.class, \"value\"); // warning: No field named 'value' found in class 'B'\n }\n \n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "AtomicFieldUpdaterIssues", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryBoxing", + "shortDescription": { + "text": "Unnecessary boxing" + }, + "fullDescription": { + "text": "Reports explicit boxing, that is wrapping of primitive values in objects. Explicit manual boxing is unnecessary as of Java 5 and later, and can safely be removed. Examples: 'Integer i = new Integer(1);' → 'Integer i = Integer.valueOf(1);' 'int i = Integer.valueOf(1);' → 'int i = 1;' Use the Only report truly superfluously boxed expressions option to report only truly superfluous boxing, where a boxed value is immediately unboxed either implicitly or explicitly. In this case, the entire boxing-unboxing step can be removed. The inspection doesn't report simple explicit boxing. This inspection only reports if the language level of the project or module is 5 or higher.", + "markdown": "Reports explicit boxing, that is wrapping of primitive values in objects.\n\nExplicit manual boxing is unnecessary as of Java 5 and later, and can safely be removed.\n\n**Examples:**\n\n* `Integer i = new Integer(1);` → `Integer i = Integer.valueOf(1);`\n* `int i = Integer.valueOf(1);` → `int i = 1;`\n\n\nUse the **Only report truly superfluously boxed expressions** option to report only truly superfluous boxing,\nwhere a boxed value is immediately unboxed either implicitly or explicitly.\nIn this case, the entire boxing-unboxing step can be removed. The inspection doesn't report simple explicit boxing.\n\nThis inspection only reports if the language level of the project or module is 5 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryBoxing", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 5", + "index": 53, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BadExceptionThrown", + "shortDescription": { + "text": "Prohibited exception thrown" + }, + "fullDescription": { + "text": "Reports 'throw' statements that throw an inappropriate exception. For example an exception can be inappropriate because it is overly generic, such as 'java.lang.Exception' or 'java.io.IOException'. Example: 'void setup(Mode mode) {\n if (mode == null)\n throw new RuntimeException(\"Problem during setup\"); // warning: Prohibited exception 'RuntimeException' thrown\n ...\n }' Use the Prohibited exceptions list to specify which exceptions should be reported.", + "markdown": "Reports `throw` statements that throw an inappropriate exception. For example an exception can be inappropriate because it is overly generic, such as `java.lang.Exception` or `java.io.IOException`.\n\n**Example:**\n\n\n void setup(Mode mode) {\n if (mode == null)\n throw new RuntimeException(\"Problem during setup\"); // warning: Prohibited exception 'RuntimeException' thrown\n ...\n }\n\nUse the **Prohibited exceptions** list to specify which exceptions should be reported." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ProhibitedExceptionThrown", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PackageNamingConvention", + "shortDescription": { + "text": "Package naming convention" + }, + "fullDescription": { + "text": "Reports packages whose names are either too short, too long, or do not follow the specified regular expression pattern. Example: 'package io;' Use the options to specify the minimum and maximum length of the package name as well as a regular expression that matches valid package names (regular expressions are in standard 'java.util.regex' format).", + "markdown": "Reports packages whose names are either too short, too long, or do not follow the specified regular expression pattern.\n\n**Example:**\n\n\n package io;\n\n\nUse the options to specify the minimum and maximum length of the package name\nas well as a regular expression that matches valid package names\n(regular expressions are in standard `java.util.regex` format)." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PackageNamingConvention", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions", + "index": 51, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Anonymous2MethodRef", + "shortDescription": { + "text": "Anonymous type can be replaced with method reference" + }, + "fullDescription": { + "text": "Reports anonymous classes which can be replaced with method references. Note that if an anonymous class is converted into an unbound method reference, the same method reference object can be reused by the Java runtime during subsequent invocations. On the other hand, when an anonymous class is used, separate objects are created every time. Thus, applying the quick-fix can cause the semantics change in rare cases, e.g. when anonymous class instances are used as 'HashMap' keys. Example: 'Runnable r = new Runnable() {\n @Override\n public void run() {\n System.out.println();\n }\n };' The quick-fix changes this code to the compact form: 'Runnable r = System.out::println;'. Use the Report when interface is not annotated with @FunctionalInterface option to enable this inspection for interfaces which are not annotated with @FunctionalInterface. This inspection only reports if the language level of the project or module is 8 or higher.", + "markdown": "Reports anonymous classes which can be replaced with method references.\n\n\nNote that if an anonymous class is converted into an unbound method reference, the same method reference object\ncan be reused by the Java runtime during subsequent invocations. On the other hand, when an anonymous class is used,\nseparate objects are created every time. Thus, applying the quick-fix can cause the semantics change in rare cases,\ne.g. when anonymous class instances are used as `HashMap` keys.\n\n**Example:**\n\n\n Runnable r = new Runnable() {\n @Override\n public void run() {\n System.out.println();\n }\n };\n\nThe quick-fix changes this code to the compact form: `Runnable r = System.out::println;`.\n\nUse the **Report when interface is not annotated with @FunctionalInterface** option to enable this inspection for\ninterfaces which are not annotated with @FunctionalInterface.\n\nThis inspection only reports if the language level of the project or module is 8 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "Anonymous2MethodRef", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 8", + "index": 62, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PublicMethodNotExposedInInterface", + "shortDescription": { + "text": "'public' method not exposed in interface" + }, + "fullDescription": { + "text": "Reports 'public' methods in classes which are not exposed in an interface. Exposing all 'public' methods via an interface is important for maintaining loose coupling, and may be necessary for certain component-based programming styles. Example: 'interface Person {\n String getName();\n}\n\nclass PersonImpl implements Person {\n private String name;\n\n // ok: method is exposed in interface\n @Override\n public String getName() {\n return name;\n }\n\n // warning: method is public\n // but not exposed in interface\n public void setName() {\n this.name = name;\n }\n}' Use the Ignore if annotated by list to specify special annotations. Methods annotated with one of these annotations will be ignored by this inspection. Use the Ignore if the containing class does not implement a non-library interface option to ignore methods from classes which do not implement any interface from the project.", + "markdown": "Reports `public` methods in classes which are not exposed in an interface.\n\nExposing all `public` methods via an interface is important for\nmaintaining loose coupling, and may be necessary for certain component-based programming styles.\n\nExample:\n\n\n interface Person {\n String getName();\n }\n\n class PersonImpl implements Person {\n private String name;\n\n // ok: method is exposed in interface\n @Override\n public String getName() {\n return name;\n }\n\n // warning: method is public\n // but not exposed in interface\n public void setName() {\n this.name = name;\n }\n }\n\n\nUse the **Ignore if annotated by** list to specify special annotations. Methods annotated with one of\nthese annotations will be ignored by this inspection.\n\n\nUse the **Ignore if the containing class does not implement a non-library interface** option to ignore methods from classes which do not\nimplement any interface from the project." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PublicMethodNotExposedInInterface", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Abstraction issues", + "index": 78, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SerializableHasSerialVersionUIDField", + "shortDescription": { + "text": "Serializable class without 'serialVersionUID'" + }, + "fullDescription": { + "text": "Reports classes that implement 'Serializable' and do not declare a 'serialVersionUID' field. Without a 'serialVersionUID' field, any change to the class will make previously serialized versions unreadable. Example: 'class Main implements Serializable {\n }' After the quick-fix is applied: 'class Main implements Serializable {\n private static final long serialVersionUID = -1446398935944895849L;\n }' When using a language level of JDK 14 or higher, the quickfix will also add the 'java.io.Serial' annotation. Use the following options to configure the inspection: List classes whose inheritors should not be reported by this inspection. This is meant for classes that inherit 'Serializable' from a superclass but are not intended for serialization. Whether to ignore 'Serializable' anonymous classes.", + "markdown": "Reports classes that implement `Serializable` and do not declare a `serialVersionUID` field.\n\n\nWithout a `serialVersionUID` field, any change to the class will make previously serialized versions unreadable.\n\n**Example:**\n\n\n class Main implements Serializable {\n }\n\nAfter the quick-fix is applied:\n\n\n class Main implements Serializable {\n private static final long serialVersionUID = -1446398935944895849L;\n }\n\nWhen using a language level of JDK 14 or higher, the quickfix will also add the `java.io.Serial` annotation.\n\nUse the following options to configure the inspection:\n\n* List classes whose inheritors should not be reported by this inspection. This is meant for classes that inherit `Serializable` from a superclass but are not intended for serialization.\n* Whether to ignore `Serializable` anonymous classes." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "serial", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages", + "index": 2, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ThrowableNotThrown", + "shortDescription": { + "text": "'Throwable' not thrown" + }, + "fullDescription": { + "text": "Reports instantiations of 'Throwable' or its subclasses, where the created 'Throwable' is never actually thrown. Additionally, this inspection reports method calls that return instances of 'Throwable' or its subclasses, when the result of the method call is not thrown. Calls to methods annotated with the Error Prone's or AssertJ's '@CanIgnoreReturnValue' annotation will not be reported. Example: 'void check(String s) {\n if (s == null) {\n new NullPointerException(\"s\");\n }\n // ...\n }'", + "markdown": "Reports instantiations of `Throwable` or its subclasses, where the created `Throwable` is never actually thrown. Additionally, this inspection reports method calls that return instances of `Throwable` or its subclasses, when the result of the method call is not thrown.\n\nCalls to methods annotated with the Error Prone's or AssertJ's `@CanIgnoreReturnValue` annotation will not be reported.\n\n**Example:**\n\n\n void check(String s) {\n if (s == null) {\n new NullPointerException(\"s\");\n }\n // ...\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ThrowableNotThrown", + "cweIds": [ + 390, + 703 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CapturingCleaner", + "shortDescription": { + "text": "Cleaner captures object reference" + }, + "fullDescription": { + "text": "Reports 'Runnable' passed to a 'Cleaner.register()' capturing reference being registered. If the reference is captured, it will never be phantom reachable and the cleaning action will never be invoked. Possible sources of this problem: Lambda using non-static methods, fields, or 'this' itself Non-static inner class (anonymous or not) always captures this reference in java up to 18 version Instance method reference Access to outer class non-static members from non-static inner class Sample of code that will be reported: 'int fileDescriptor;\n Cleaner.Cleanable cleanable = Cleaner.create().register(this, () -> {\n System.out.println(\"adsad\");\n //this is captured via fileDescriptor\n fileDescriptor = 0;\n });' This inspection only reports if the language level of the project or module is 9 or higher. New in 2018.1", + "markdown": "Reports `Runnable` passed to a `Cleaner.register()` capturing reference being registered. If the reference is captured, it will never be phantom reachable and the cleaning action will never be invoked.\n\nPossible sources of this problem:\n\n* Lambda using non-static methods, fields, or `this` itself\n* Non-static inner class (anonymous or not) always captures this reference in java up to 18 version\n* Instance method reference\n* Access to outer class non-static members from non-static inner class\n\nSample of code that will be reported:\n\n\n int fileDescriptor;\n Cleaner.Cleanable cleanable = Cleaner.create().register(this, () -> {\n System.out.println(\"adsad\");\n //this is captured via fileDescriptor\n fileDescriptor = 0;\n });\n\nThis inspection only reports if the language level of the project or module is 9 or higher.\n\nNew in 2018.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "CapturingCleaner", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ThreadStartInConstruction", + "shortDescription": { + "text": "Call to 'Thread.start()' during object construction" + }, + "fullDescription": { + "text": "Reports calls to 'start()' on 'java.lang.Thread' or any of its subclasses during object construction. While occasionally useful, such constructs should be avoided due to inheritance issues. Subclasses of a class that launches a thread during the object construction will not have finished any initialization logic of their own before the thread has launched. This inspection does not report if the class that starts a thread is declared 'final'. Example: 'class MyThread extends Thread {\n MyThread() {\n start();\n }\n }'", + "markdown": "Reports calls to `start()` on `java.lang.Thread` or any of its subclasses during object construction.\n\n\nWhile occasionally useful, such constructs should be avoided due to inheritance issues.\nSubclasses of a class that launches a thread during the object construction will not have finished\nany initialization logic of their own before the thread has launched.\n\nThis inspection does not report if the class that starts a thread is declared `final`.\n\n**Example:**\n\n\n class MyThread extends Thread {\n MyThread() {\n start();\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToThreadStartDuringObjectConstruction", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TestCaseWithNoTestMethods", + "shortDescription": { + "text": "Test class without tests" + }, + "fullDescription": { + "text": "Reports non-'abstract' test cases without any test methods. Such test cases usually indicate unfinished code or could be a refactoring leftover that should be removed. Example: 'public class CrucialTest {\n @Before\n public void setUp() {\n System.out.println(\"setting up\");\n }\n }' Use the Ignore test cases which have superclasses with test methods option to ignore test cases which have super classes with test methods.", + "markdown": "Reports non-`abstract` test cases without any test methods. Such test cases usually indicate unfinished code or could be a refactoring leftover that should be removed.\n\nExample:\n\n\n public class CrucialTest {\n @Before\n public void setUp() {\n System.out.println(\"setting up\");\n }\n }\n\n\nUse the **Ignore test cases which have superclasses with test methods** option to ignore test cases which have super classes\nwith test methods." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "JUnitTestCaseWithNoTests", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages/Test frameworks", + "index": 94, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SwitchStatementWithTooManyBranches", + "shortDescription": { + "text": "Maximum 'switch' branches" + }, + "fullDescription": { + "text": "Reports 'switch' statements or expressions with too many 'case' labels. Such a long switch statement may be confusing and should probably be refactored. Sometimes, it is not a problem (for example, a domain is very complicated and has enums with a lot of constants). Example: 'switch (x) {\n case 1 -> {}\n case 2 -> {}\n case 3 -> {}\n case 4 -> {}\n case 5 -> {}\n case 6 -> {}\n case 7 -> {}\n case 8 -> {}\n case 9 -> {}\n case 10 -> {}\n case 11,12,13 -> {}\n default -> {}\n }' Use the Maximum number of branches field to specify the maximum number of 'case' labels expected.", + "markdown": "Reports `switch` statements or expressions with too many `case` labels.\n\nSuch a long switch statement may be confusing and should probably be refactored.\nSometimes, it is not a problem (for example, a domain is very complicated and has enums with a lot of constants).\n\nExample:\n\n\n switch (x) {\n case 1 -> {}\n case 2 -> {}\n case 3 -> {}\n case 4 -> {}\n case 5 -> {}\n case 6 -> {}\n case 7 -> {}\n case 8 -> {}\n case 9 -> {}\n case 10 -> {}\n case 11,12,13 -> {}\n default -> {}\n }\n\nUse the **Maximum number of branches** field to specify the maximum number of `case` labels expected." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SwitchStatementWithTooManyBranches", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MismatchedArrayReadWrite", + "shortDescription": { + "text": "Mismatched read and write of array" + }, + "fullDescription": { + "text": "Reports arrays whose contents are read but not updated, or updated but not read. Such inconsistent reads and writes are pointless and probably indicate dead, incomplete or erroneous code. Example: 'final int[] bar = new int[3];\n bar[2] = 3;'", + "markdown": "Reports arrays whose contents are read but not updated, or updated but not read. Such inconsistent reads and writes are pointless and probably indicate dead, incomplete or erroneous code.\n\n**Example:**\n\n\n final int[] bar = new int[3];\n bar[2] = 3;\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "MismatchedReadAndWriteOfArray", + "cweIds": [ + 561, + 563 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessarilyQualifiedStaticUsage", + "shortDescription": { + "text": "Unnecessarily qualified static access" + }, + "fullDescription": { + "text": "Reports usages of static members qualified with the class name. Such qualification is unnecessary and may be safely removed. Example: 'class Foo {\n static void foo() {}\n static int x;\n\n void bar() {\n Foo.foo();\n System.out.println(Foo.x);\n }\n\n static void baz() { Foo.foo(); }\n }' After the quick-fix is applied: 'class Foo {\n static void foo() {}\n static int x;\n\n void bar() {\n foo();\n System.out.println(x);\n }\n\n static void baz() { foo(); }\n }' Use the inspection options to toggle the reporting for: Static fields access: 'void bar() { System.out.println(Foo.x); }' Calls to static methods: 'void bar() { Foo.foo(); }' Also, you can configure the inspection to only report static member usage in a static context. In this case, only 'static void baz() { Foo.foo(); }' will be reported.", + "markdown": "Reports usages of static members qualified with the class name.\n\n\nSuch qualification is unnecessary and may be safely removed.\n\n**Example:**\n\n\n class Foo {\n static void foo() {}\n static int x;\n\n void bar() {\n Foo.foo();\n System.out.println(Foo.x);\n }\n\n static void baz() { Foo.foo(); }\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo {\n static void foo() {}\n static int x;\n\n void bar() {\n foo();\n System.out.println(x);\n }\n\n static void baz() { foo(); }\n }\n\n\nUse the inspection options to toggle the reporting for:\n\n* Static fields access: \n `void bar() { System.out.println(Foo.x); }`\n\n* Calls to static methods: \n `void bar() { Foo.foo(); }`\n\n\nAlso, you can configure the inspection to only report static member usage\nin a static context. In this case, only `static void baz() { Foo.foo(); }` will be reported." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessarilyQualifiedStaticUsage", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LoopConditionNotUpdatedInsideLoop", + "shortDescription": { + "text": "Loop variable not updated inside loop" + }, + "fullDescription": { + "text": "Reports any variables and parameters that are used in a loop condition and are not updated inside the loop. Such variables and parameters are usually used by mistake as they may cause an infinite loop if they are executed. Example: 'void loopDoesNotLoop(boolean b) {\n while (b) {\n System.out.println();\n break;\n }\n }' Configure the inspection: Use the Ignore possible non-local changes option to disable this inspection if the condition can be updated indirectly (e.g. via the called method or concurrently from another thread).", + "markdown": "Reports any variables and parameters that are used in a loop condition and are not updated inside the loop.\n\nSuch variables and parameters are usually used by mistake as they\nmay cause an infinite loop if they are executed.\n\nExample:\n\n\n void loopDoesNotLoop(boolean b) {\n while (b) {\n System.out.println();\n break;\n }\n }\n\nConfigure the inspection:\n\n\nUse the **Ignore possible non-local changes** option to disable this inspection\nif the condition can be updated indirectly (e.g. via the called method or concurrently from another thread)." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "LoopConditionNotUpdatedInsideLoop", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CollectionsMustHaveInitialCapacity", + "shortDescription": { + "text": "Collection without initial capacity" + }, + "fullDescription": { + "text": "Reports attempts to instantiate a new 'Collection' object without specifying an initial capacity. If no initial capacity is specified, a default capacity is used, which will rarely be optimal. Failing to specify initial capacities for collections may result in performance issues if space needs to be reallocated and memory copied when the initial capacity is exceeded. This inspection checks allocations of classes listed in the inspection's settings. Example: 'new HashMap();' Use the following options to configure the inspection: List collection classes that should be checked. Whether to ignore field initializers.", + "markdown": "Reports attempts to instantiate a new `Collection` object without specifying an initial capacity.\n\n\nIf no initial capacity is specified, a default capacity is used, which will rarely be optimal. Failing\nto specify initial capacities for collections may result in performance issues if space needs to be reallocated and\nmemory copied when the initial capacity is exceeded.\nThis inspection checks allocations of classes listed in the inspection's settings.\n\n**Example:**\n\n\n new HashMap();\n\nUse the following options to configure the inspection:\n\n* List collection classes that should be checked.\n* Whether to ignore field initializers." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CollectionWithoutInitialCapacity", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SystemGetProperty", + "shortDescription": { + "text": "Call to 'System.getProperty(str)' could be simplified" + }, + "fullDescription": { + "text": "Reports the usage of method 'System.getProperty(str)' and suggests a fix in 2 cases: 'System.getProperty(\"path.separator\")' -> 'File.pathSeparator' 'System.getProperty(\"line.separator\")' -> 'System.lineSeparator()' The second one is not only less error-prone but is likely to be faster, as 'System.lineSeparator()' returns cached value, while 'System.getProperty(\"line.separator\")' each time calls to Properties (Hashtable or CHM depending on implementation).", + "markdown": "Reports the usage of method `System.getProperty(str)` and suggests a fix in 2 cases:\n\n* `System.getProperty(\"path.separator\")` -\\> `File.pathSeparator`\n* `System.getProperty(\"line.separator\")` -\\> `System.lineSeparator()`\n\nThe second one is not only less error-prone but is likely to be faster, as `System.lineSeparator()` returns cached value, while `System.getProperty(\"line.separator\")` each time calls to Properties (Hashtable or CHM depending on implementation)." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SystemGetProperty", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages", + "index": 2, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DoubleCheckedLocking", + "shortDescription": { + "text": "Double-checked locking" + }, + "fullDescription": { + "text": "Reports double-checked locking. Double-checked locking tries to initialize a field on demand and in a thread-safe manner, while avoiding the cost of synchronization. Unfortunately it is not thread-safe when used on a field that is not declared 'volatile'. When using Java 1.4 or earlier, double-checked locking doesn't work even with a 'volatile' field. Read the article linked above for a detailed explanation of the problem. Example of incorrect double-checked locking: 'class Foo {\n private Helper helper = null;\n public Helper getHelper() {\n if (helper == null)\n synchronized(this) {\n if (helper == null) helper = new Helper();\n }\n return helper;\n }\n }\n // other functions and members...\n }'", + "markdown": "Reports [double-checked locking](https://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html).\n\n\nDouble-checked locking tries to initialize a field on demand and in a thread-safe manner, while avoiding the cost of synchronization.\nUnfortunately it is not thread-safe when used on a field that is not declared `volatile`.\nWhen using Java 1.4 or earlier, double-checked locking doesn't work even with a `volatile` field.\nRead the article linked above for a detailed explanation of the problem.\n\nExample of incorrect double-checked locking:\n\n\n class Foo {\n private Helper helper = null;\n public Helper getHelper() {\n if (helper == null)\n synchronized(this) {\n if (helper == null) helper = new Helper();\n }\n return helper;\n }\n }\n // other functions and members...\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "DoubleCheckedLocking", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassOnlyUsedInOneModule", + "shortDescription": { + "text": "Class only used from one other module" + }, + "fullDescription": { + "text": "Reports classes that: do not depend on any other class in their module depend on classes from a different module are a dependency only for classes from this other module Such classes could be moved into the module on which they depend. Available only from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name and isn't reported in the editor.", + "markdown": "Reports classes that:\n\n* do not depend on any other class in their module\n* depend on classes from a different module\n* are a dependency only for classes from this other module\n\nSuch classes could be moved into the module on which they depend.\n\nAvailable only from **Code \\| Inspect Code** or\n**Code \\| Analyze Code \\| Run Inspection by Name** and isn't reported in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassOnlyUsedInOneModule", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Modularization issues", + "index": 69, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TryStatementWithMultipleResources", + "shortDescription": { + "text": "'try' statement with multiple resources can be split" + }, + "fullDescription": { + "text": "Reports 'try' statements with multiple resources that can be automatically split into multiple try-with-resources statements. This conversion can be useful for further refactoring (for example, for extracting the nested 'try' statement into a separate method). Example: 'try (FileInputStream in = new FileInputStream(\"in.txt\");\n FileOutputStream out = new FileOutputStream(\"out.txt\")) {\n /*read and write*/\n }' After the quick-fix is applied: 'try (FileInputStream in = new FileInputStream(\"in.txt\")) {\n try (FileOutputStream out = new FileOutputStream(\"out.txt\")) {\n /*read and write*/\n }\n }'", + "markdown": "Reports `try` statements with multiple resources that can be automatically split into multiple try-with-resources statements.\n\nThis conversion can be useful for further refactoring\n(for example, for extracting the nested `try` statement into a separate method).\n\nExample:\n\n\n try (FileInputStream in = new FileInputStream(\"in.txt\");\n FileOutputStream out = new FileOutputStream(\"out.txt\")) {\n /*read and write*/\n }\n\nAfter the quick-fix is applied:\n\n\n try (FileInputStream in = new FileInputStream(\"in.txt\")) {\n try (FileOutputStream out = new FileOutputStream(\"out.txt\")) {\n /*read and write*/\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "TryStatementWithMultipleResources", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TypeMayBeWeakened", + "shortDescription": { + "text": "Type may be weakened" + }, + "fullDescription": { + "text": "Reports variable and method return types that can be changed to a more abstract (weaker) type. This allows making the code more abstract, hence more reusable. Example: '// Type of parameter can be weakened to java.util.List\n void processList(ArrayList list) {\n if (list.isEmpty()) return;\n System.out.println(\"Processing\");\n for (String s : list) {\n System.out.println(\"String: \" + s);\n }\n }' Enable the Only weaken to an interface checkbox below to only report a problem when the type can be weakened to an interface type. Enable the Do not suggest weakening variable declared as 'var' checkbox below to prevent reporting on local variables declared using the 'var' keyword (Java 10+) Stop classes are intended to prevent weakening to classes lower than stop classes, even if it is possible. In some cases, this may improve readability.", + "markdown": "Reports variable and method return types that can be changed to a more abstract (weaker) type. This allows making the code more abstract, hence more reusable.\n\nExample:\n\n\n // Type of parameter can be weakened to java.util.List\n void processList(ArrayList list) {\n if (list.isEmpty()) return;\n System.out.println(\"Processing\");\n for (String s : list) {\n System.out.println(\"String: \" + s);\n }\n }\n\n\nEnable the **Only weaken to an interface** checkbox below\nto only report a problem when the type can be weakened to an interface type.\n\n\nEnable the **Do not suggest weakening variable declared as 'var'** checkbox below\nto prevent reporting on local variables declared using the 'var' keyword (Java 10+)\n\n\n**Stop classes** are intended to prevent weakening to classes\nlower than stop classes, even if it is possible.\nIn some cases, this may improve readability." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "TypeMayBeWeakened", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Abstraction issues", + "index": 78, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CloneableImplementsClone", + "shortDescription": { + "text": "Cloneable class without 'clone()' method" + }, + "fullDescription": { + "text": "Reports classes implementing the 'Cloneable' interface that don't override the 'clone()' method. Such classes use the default implementation of 'clone()', which isn't 'public' but 'protected', and which does not copy the mutable state of the class. A quick-fix is available to generate a basic 'clone()' method, which can be used as a basis for a properly functioning 'clone()' method expected from a 'Cloneable' class. Example: 'public class Data implements Cloneable {\n private String[] names;\n }' After the quick-fix is applied: 'public class Data implements Cloneable {\n private String[] names;\n\n @Override\n public Data clone() {\n try {\n Data clone = (Data) super.clone();\n // TODO: copy mutable state here, so the clone can't change the internals of the original\n return clone;\n } catch (CloneNotSupportedException e) {\n throw new AssertionError();\n }\n }\n }' Use the Ignore classes cloneable due to inheritance option to ignore classes that are 'Cloneable' because they inherit from the 'Cloneable' class. Use the Ignore when Cloneable is necessary to call clone() method of super class option to ignore classes that require implementing 'Cloneable' because they call the 'clone()' method from a superclass.", + "markdown": "Reports classes implementing the `Cloneable` interface that don't override the `clone()` method.\n\nSuch classes use the default implementation of `clone()`,\nwhich isn't `public` but `protected`, and which does not copy the mutable state of the class.\n\nA quick-fix is available to generate a basic `clone()` method,\nwhich can be used as a basis for a properly functioning `clone()` method\nexpected from a `Cloneable` class.\n\n**Example:**\n\n\n public class Data implements Cloneable {\n private String[] names;\n }\n\nAfter the quick-fix is applied:\n\n\n public class Data implements Cloneable {\n private String[] names;\n\n @Override\n public Data clone() {\n try {\n Data clone = (Data) super.clone();\n // TODO: copy mutable state here, so the clone can't change the internals of the original\n return clone;\n } catch (CloneNotSupportedException e) {\n throw new AssertionError();\n }\n }\n }\n\nUse the **Ignore classes cloneable due to inheritance** option to ignore classes that are\n`Cloneable` because they inherit from the `Cloneable` class.\n\nUse the **Ignore when Cloneable is necessary to call clone() method of super class**\noption to ignore classes that require implementing `Cloneable` because they call the `clone()` method from a superclass." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "CloneableClassWithoutClone", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Cloning issues", + "index": 82, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OctalAndDecimalIntegersMixed", + "shortDescription": { + "text": "Octal and decimal integers in same array" + }, + "fullDescription": { + "text": "Reports mixed octal and decimal integer literals in a single array initializer. This situation might happen when you copy a list of numbers into an array initializer. Some numbers in the array might be zero-padded and the compiler will interpret them as octal. Example: 'int[] elapsed = {1, 13, 052};' After the quick-fix that removes a leading zero is applied: 'int[] elapsed = {1, 13, 52};' If it is an octal number (for example, after a variable inline), then you can use another quick-fix that converts octal to decimal: 'int[] elapsed = {1, 13, 42};'", + "markdown": "Reports mixed octal and decimal integer literals in a single array initializer. This situation might happen when you copy a list of numbers into an array initializer. Some numbers in the array might be zero-padded and the compiler will interpret them as octal.\n\n**Example:**\n\n int[] elapsed = {1, 13, 052};\n\nAfter the quick-fix that removes a leading zero is applied:\n\n int[] elapsed = {1, 13, 52};\n\nIf it is an octal number (for example, after a variable inline), then you can use another quick-fix that converts octal to decimal:\n`int[] elapsed = {1, 13, 42};`" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OctalAndDecimalIntegersInSameArray", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UseOfObsoleteAssert", + "shortDescription": { + "text": "Usage of obsolete 'junit.framework.Assert' method" + }, + "fullDescription": { + "text": "Reports any calls to methods from the 'junit.framework.Assert' class. This class is obsolete and the calls can be replaced by calls to methods from the 'org.junit.Assert' class. For example: 'import org.junit.*;\n public class NecessaryTest {\n @Test\n public void testIt() {\n junit.framework.Assert.assertEquals(\"expected\", \"actual\");\n }\n }' After the quick fix is applied, the result looks like the following: 'import org.junit;\n public class NecessaryTest {\n\n public void testIt() {\n Assert.assertEquals(\"expected\", \"actual\");\n }\n }'", + "markdown": "Reports any calls to methods from the `junit.framework.Assert` class. This class is obsolete and the calls can be replaced by calls to methods from the `org.junit.Assert` class.\n\nFor example:\n\n\n import org.junit.*;\n public class NecessaryTest {\n @Test\n public void testIt() {\n junit.framework.Assert.assertEquals(\"expected\", \"actual\");\n }\n }\n\nAfter the quick fix is applied, the result looks like the following:\n\n\n import org.junit;\n public class NecessaryTest {\n\n public void testIt() {\n Assert.assertEquals(\"expected\", \"actual\");\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UseOfObsoleteAssert", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/JUnit", + "index": 77, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Deprecation", + "shortDescription": { + "text": "Deprecated API usage" + }, + "fullDescription": { + "text": "Reports usages of deprecated classes, fields, and methods. A quick-fix is available to automatically convert the deprecated usage, when the necessary information can be extracted from the Javadoc of the deprecated member. Example: 'class Interesting {\n\n /**\n * @deprecated Use {@link #newHotness()} instead\n */\n @Deprecated\n public void oldAndBusted() {}\n\n public void newHotness() {}\n }\n class ElseWhere {\n void x(Interesting i) {\n i.oldAndBusted(); // deprecated warning here\n }\n }' After the quick-fix is applied: 'class Interesting {\n\n /**\n * @deprecated Use {@link #newHotness()} instead\n */\n @Deprecated\n public void oldAndBusted() {}\n\n public void newHotness() {}\n }\n class ElseWhere {\n void x(Interesting i) {\n i.newHotness();\n }\n }' By default, the inspection doesn't produce a warning if it's impossible or hard to avoid it. For example, the following code won't be reported: 'abstract class A { //library code\n @Deprecated\n abstract void m();\n }\n class B extends A { //project code\n @Override\n void m() {\n //doSmth;\n }\n }' Configure the inspection: Use the options to disable this inspection inside deprecated members, overrides of abstract deprecated methods, non-static import statements, methods of deprecated classes, or same top-level classes.", + "markdown": "Reports usages of deprecated classes, fields, and methods. A quick-fix is available to automatically convert the deprecated usage, when the necessary information can be extracted from the Javadoc of the deprecated member.\n\n**Example:**\n\n\n class Interesting {\n\n /**\n * @deprecated Use {@link #newHotness()} instead\n */\n @Deprecated\n public void oldAndBusted() {}\n\n public void newHotness() {}\n }\n class ElseWhere {\n void x(Interesting i) {\n i.oldAndBusted(); // deprecated warning here\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Interesting {\n\n /**\n * @deprecated Use {@link #newHotness()} instead\n */\n @Deprecated\n public void oldAndBusted() {}\n\n public void newHotness() {}\n }\n class ElseWhere {\n void x(Interesting i) {\n i.newHotness();\n }\n }\n\nBy default, the inspection doesn't produce a warning if it's impossible or hard to avoid it. For example,\nthe following code won't be reported:\n\n\n abstract class A { //library code\n @Deprecated\n abstract void m();\n }\n class B extends A { //project code\n @Override\n void m() {\n //doSmth;\n }\n }\n\nConfigure the inspection:\n\n\nUse the options to disable this inspection inside deprecated members,\noverrides of abstract deprecated methods, non-static import statements, methods of deprecated classes, or same top-level classes." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "deprecation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code maturity", + "index": 50, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MisspelledMethodName", + "shortDescription": { + "text": "Method names differing only by case" + }, + "fullDescription": { + "text": "Reports cases in which multiple methods of a class have the names that differ only by case. Such names may be very confusing. Example: 'public int hashcode() { // reported, should be hashCode probably?\n return 0;\n }' A quick-fix that renames such methods is available only in the editor. Use the Ignore methods overriding/implementing a super method option to ignore methods overriding or implementing a method from the superclass.", + "markdown": "Reports cases in which multiple methods of a class have the names that differ only by case. Such names may be very confusing.\n\n**Example:**\n\n\n public int hashcode() { // reported, should be hashCode probably?\n return 0;\n }\n\nA quick-fix that renames such methods is available only in the editor.\n\nUse the **Ignore methods overriding/implementing a super method** option to ignore methods overriding or implementing a method from\nthe superclass." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MethodNamesDifferingOnlyByCase", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions/Method", + "index": 88, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonSerializableObjectBoundToHttpSession", + "shortDescription": { + "text": "Non-serializable object bound to 'HttpSession'" + }, + "fullDescription": { + "text": "Reports objects of classes not implementing 'java.io.Serializable' used as arguments to 'javax.servlet.http.HttpSession.setAttribute()' or 'javax.servlet.http.HttpSession.putValue()'. Such objects will not be serialized if the 'HttpSession' is passivated or migrated, and may result in difficult-to-diagnose bugs. This inspection assumes objects of the types 'java.util.Collection' and 'java.util.Map' to be 'Serializable', unless type parameters are non-'Serializable'. Example: 'void foo(HttpSession session) {\n session.setAttribute(\"foo\", new NonSerializable());\n }\n static class NonSerializable {}'", + "markdown": "Reports objects of classes not implementing `java.io.Serializable` used as arguments to `javax.servlet.http.HttpSession.setAttribute()` or `javax.servlet.http.HttpSession.putValue()`.\n\n\nSuch objects will not be serialized if the `HttpSession` is passivated or migrated,\nand may result in difficult-to-diagnose bugs.\n\n\nThis inspection assumes objects of the types `java.util.Collection` and\n`java.util.Map` to be `Serializable`,\nunless type parameters are non-`Serializable`.\n\n**Example:**\n\n\n void foo(HttpSession session) {\n session.setAttribute(\"foo\", new NonSerializable());\n }\n static class NonSerializable {}\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonSerializableObjectBoundToHttpSession", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ThreadLocalNotStaticFinal", + "shortDescription": { + "text": "'ThreadLocal' field not declared 'static final'" + }, + "fullDescription": { + "text": "Reports fields of type 'java.lang.ThreadLocal' that are not declared 'static final'. In the most common case, a 'java.lang.ThreadLocal' instance associates state with a thread. A non-static non-final 'java.lang.ThreadLocal' field associates state with an instance-thread combination. This is usually unnecessary and quite often is a bug that can cause memory leaks and incorrect behavior. A quick-fix is suggested to make the field 'static final'. Example: 'private ThreadLocal tl = ThreadLocal.withInitial(() -> Boolean.TRUE);'", + "markdown": "Reports fields of type `java.lang.ThreadLocal` that are not declared `static final`.\n\n\nIn the most common case, a `java.lang.ThreadLocal` instance associates state with a thread.\nA non-static non-final `java.lang.ThreadLocal` field associates state with an instance-thread combination.\nThis is usually unnecessary and quite often is a bug that can cause memory leaks and incorrect behavior.\n\n\nA quick-fix is suggested to make the field `static final`.\n\n\n**Example:**\n\n\n private ThreadLocal tl = ThreadLocal.withInitial(() -> Boolean.TRUE);\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ThreadLocalNotStaticFinal", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConditionSignal", + "shortDescription": { + "text": "Call to 'signal()' instead of 'signalAll()'" + }, + "fullDescription": { + "text": "Reports calls to 'java.util.concurrent.locks.Condition.signal()'. While occasionally useful, in almost all cases 'signalAll()' is a better and safer choice.", + "markdown": "Reports calls to `java.util.concurrent.locks.Condition.signal()`. While occasionally useful, in almost all cases `signalAll()` is a better and safer choice." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToSignalInsteadOfSignalAll", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AccessStaticViaInstance", + "shortDescription": { + "text": "Access static member via instance reference" + }, + "fullDescription": { + "text": "Reports references to 'static' methods and fields via a class instance rather than the class itself. Even though referring to static members via instance variables is allowed by The Java Language Specification, this makes the code confusing as the reader may think that the result of the method depends on the instance. The quick-fix replaces the instance variable with the class name. Example: 'String s1 = s.valueOf(0);' After the quick-fix is applied: 'String s = String.valueOf(0);'", + "markdown": "Reports references to `static` methods and fields via a class instance rather than the class itself.\n\nEven though referring to static members via instance variables is allowed by The Java Language Specification,\nthis makes the code confusing as the reader may think that the result of the method depends on the instance.\n\nThe quick-fix replaces the instance variable with the class name.\n\nExample:\n\n\n String s1 = s.valueOf(0);\n\nAfter the quick-fix is applied:\n\n\n String s = String.valueOf(0);\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AccessStaticViaInstance", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PublicMethodWithoutLogging", + "shortDescription": { + "text": "'public' method without logging" + }, + "fullDescription": { + "text": "Reports any public methods that do not contain a logging statement. This inspection does not report simple getters and setters. For example: 'public class Crucial {\n private static final Logger LOG = LoggerFactory.getLogger(Crucial.class);\n public void doImportantStuff() {\n // warning on this method\n }\n\n public void doOtherStuff() {\n LOG.info(\"do other stuff\");\n }\n }' Use the table below to specify Logger class names. Public methods that do not use instance methods of the specified classes will be reported by this inspection.", + "markdown": "Reports any public methods that do not contain a logging statement. This inspection does not report simple getters and setters.\n\nFor example:\n\n\n public class Crucial {\n private static finalLogger LOG = LoggerFactory.getLogger(Crucial.class);\n public void doImportantStuff() {\n // warning on this method\n }\n\n public void doOtherStuff() {\n LOG.info(\"do other stuff\");\n }\n }\n\n\nUse the table below to specify Logger class names.\nPublic methods that do not use instance methods of the specified classes will be reported by this inspection." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PublicMethodWithoutLogging", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Logging", + "index": 68, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassNestingDepth", + "shortDescription": { + "text": "Inner class too deeply nested" + }, + "fullDescription": { + "text": "Reports classes whose number of nested inner classes exceeds the specified maximum. Nesting inner classes inside other inner classes is confusing and indicates that a refactoring may be necessary. Use the Nesting limit field to specify the maximum allowed nesting depth for a class.", + "markdown": "Reports classes whose number of nested inner classes exceeds the specified maximum.\n\nNesting inner classes inside other inner classes is confusing and indicates that a refactoring may be necessary.\n\nUse the **Nesting limit** field to specify the maximum allowed nesting depth for a class." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "InnerClassTooDeeplyNested", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class metrics", + "index": 87, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CallToNativeMethodWhileLocked", + "shortDescription": { + "text": "Call to a 'native' method while locked" + }, + "fullDescription": { + "text": "Reports calls 'native' methods within a 'synchronized' block or method. When possible, it's better to keep calls to 'native' methods out of the synchronized context because such calls cause an expensive context switch and may lead to performance issues. Example: 'native void nativeMethod();\n\n void example(){\n synchronized (lock){\n nativeMethod();//warning\n }\n }'", + "markdown": "Reports calls `native` methods within a `synchronized` block or method.\n\n\nWhen possible, it's better to keep calls to `native` methods out of the synchronized context\nbecause such calls cause an expensive context switch and may lead to performance issues.\n\n**Example:**\n\n\n native void nativeMethod();\n\n void example(){\n synchronized (lock){\n nativeMethod();//warning\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToNativeMethodWhileLocked", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Dependency", + "shortDescription": { + "text": "Illegal package dependencies" + }, + "fullDescription": { + "text": "Reports illegal dependencies between scopes according to the dependency rules given. Dependency rules can be used to prohibit usage from a scope to another scope. Use the Configure dependency rules button below to customize validation rules.", + "markdown": "Reports illegal dependencies between scopes according to the dependency rules given. Dependency rules can be used to prohibit usage from a scope to another scope.\n\nUse the **Configure dependency rules** button below to customize validation rules." + }, + "defaultConfiguration": { + "enabled": true, + "level": "error", + "parameters": { + "suppressToolId": "Dependency", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages", + "index": 2, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LambdaParameterTypeCanBeSpecified", + "shortDescription": { + "text": "Lambda parameter type can be specified" + }, + "fullDescription": { + "text": "Reports lambda parameters that do not have their type specified and suggests adding the missing type declarations. Example: 'Function length = a -> a.length();' After the quick-fix is applied: 'Function length = (String a) -> a.length();' This inspection only reports if the language level of the project or module is 8 or higher.", + "markdown": "Reports lambda parameters that do not have their type specified and suggests adding the missing type declarations.\n\nExample:\n\n\n Function length = a -> a.length();\n\nAfter the quick-fix is applied:\n\n\n Function length = (String a) -> a.length();\n\nThis inspection only reports if the language level of the project or module is 8 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "LambdaParameterTypeCanBeSpecified", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NestingDepth", + "shortDescription": { + "text": "Overly nested method" + }, + "fullDescription": { + "text": "Reports methods whose body contain too deeply nested statements. Methods with too deep statement nesting may be confusing and are a good sign that refactoring may be necessary. Use the Nesting depth limit field to specify the maximum allowed nesting depth for a method.", + "markdown": "Reports methods whose body contain too deeply nested statements.\n\nMethods with too deep statement\nnesting may be confusing and are a good sign that refactoring may be necessary.\n\nUse the **Nesting depth limit** field to specify the maximum allowed nesting depth for a method." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OverlyNestedMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Method metrics", + "index": 95, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantExplicitChronoField", + "shortDescription": { + "text": "Calls of 'java.time' methods with explicit 'ChronoField' or 'ChronoUnit' arguments can be simplified" + }, + "fullDescription": { + "text": "Reports 'java.time' method calls with 'java.time.temporal.ChronoField' and 'java.time.temporal.ChronoUnit' as arguments when these calls can be replaced with calls of more specific methods. Example: 'LocalTime localTime = LocalTime.now();\nint minute = localTime.get(ChronoField.MINUTE_OF_HOUR);' After the quick-fix is applied: 'LocalTime localTime = LocalTime.now();\nint minute = localTime.getMinute();' New in 2023.2", + "markdown": "Reports `java.time` method calls with `java.time.temporal.ChronoField` and `java.time.temporal.ChronoUnit` as arguments when these calls can be replaced with calls of more specific methods.\n\nExample:\n\n\n LocalTime localTime = LocalTime.now();\n int minute = localTime.get(ChronoField.MINUTE_OF_HOUR);\n\nAfter the quick-fix is applied:\n\n\n LocalTime localTime = LocalTime.now();\n int minute = localTime.getMinute();\n\nNew in 2023.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantExplicitChronoField", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TextLabelInSwitchStatement", + "shortDescription": { + "text": "Text label in 'switch' statement" + }, + "fullDescription": { + "text": "Reports labeled statements inside of 'switch' statements. While occasionally intended, this construction is often the result of a typo. Example: 'switch (x) {\n case 1:\n case2: //warning: Text label 'case2:' in 'switch' statement\n case 3:\n break;\n }'", + "markdown": "Reports labeled statements inside of `switch` statements. While occasionally intended, this construction is often the result of a typo.\n\n**Example:**\n\n\n switch (x) {\n case 1:\n case2: //warning: Text label 'case2:' in 'switch' statement\n case 3:\n break;\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "TextLabelInSwitchStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PackageVisibleInnerClass", + "shortDescription": { + "text": "Package-visible nested class" + }, + "fullDescription": { + "text": "Reports nested classes that are declared without any access modifier (also known as package-private). Example: 'public class Outer {\n static class Nested {} // warning\n class Inner {} // warning\n enum Mode {} // warning depends on the setting\n interface I {} // warning depends on the setting\n }' Configure the inspection: Use the Ignore package-visible inner enums option to ignore package-private inner enums. Use the Ignore package-visible inner interfaces option to ignore package-private inner interfaces.", + "markdown": "Reports nested classes that are declared without any access modifier (also known as package-private).\n\n**Example:**\n\n\n public class Outer {\n static class Nested {} // warning\n class Inner {} // warning\n enum Mode {} // warning depends on the setting\n interface I {} // warning depends on the setting\n }\n\nConfigure the inspection:\n\n* Use the **Ignore package-visible inner enums** option to ignore package-private inner enums.\n* Use the **Ignore package-visible inner interfaces** option to ignore package-private inner interfaces." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PackageVisibleInnerClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Encapsulation", + "index": 60, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryUnaryMinus", + "shortDescription": { + "text": "Unnecessary unary minus" + }, + "fullDescription": { + "text": "Reports unnecessary unary minuses. Such expressions might be hard to understand and might contain errors. For example: 'void unaryMinus(int i) {\n int x = - -i;\n }' The following quick fixes are suggested here: Remove '-' operators before the 'i' variable: 'void unaryMinus(int i) {\n int x = i;\n }' Replace '-' operators with the prefix decrement operator: 'void unaryMinus(int i) {\n int x = --i;\n }' Another example: 'void unaryMinus(int i) {\n i += - 8;\n }' After the quick-fix is applied: 'void unaryMinus(int i) {\n i -= 8;\n }'", + "markdown": "Reports unnecessary unary minuses. Such expressions might be hard to understand and might contain errors.\n\n**For example:**\n\n void unaryMinus(int i) {\n int x = - -i;\n }\n\nThe following quick fixes are suggested here:\n\n* Remove `-` operators before the `i` variable:\n\n void unaryMinus(int i) {\n int x = i;\n }\n\n* Replace `-` operators with the prefix decrement operator:\n\n void unaryMinus(int i) {\n int x = --i;\n }\n\n**Another example:**\n\n void unaryMinus(int i) {\n i += - 8;\n }\n\nAfter the quick-fix is applied:\n\n void unaryMinus(int i) {\n i -= 8;\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryUnaryMinus", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "WriteOnlyObject", + "shortDescription": { + "text": "Write-only object" + }, + "fullDescription": { + "text": "Reports objects that are modified but never queried. The inspection relies on the method mutation contract, which could be inferred or pre-annotated for some library methods. This inspection does not report collections, maps, and string builders, as these types are reported by other more precise inspections. Example: 'AtomicReference ref = new AtomicReference<>();\n ref.set(\"hello\"); // ref is never used again' Use the Ignore impure constructors option to control whether to process objects created by constructor or method whose purity is not known. Unchecking the option may introduce some false-positives if the object reference is intentionally leaked during the construction. New in 2021.2", + "markdown": "Reports objects that are modified but never queried.\n\nThe inspection relies on the method mutation contract, which could be inferred\nor pre-annotated for some library methods. This inspection does not report collections, maps, and string builders, as these types\nare reported by other more precise inspections.\n\nExample:\n\n\n AtomicReference ref = new AtomicReference<>();\n ref.set(\"hello\"); // ref is never used again\n\n\nUse the **Ignore impure constructors** option to control whether to process objects created by constructor or method whose purity is not known.\nUnchecking the option may introduce some false-positives if the object reference is intentionally leaked during the construction.\n**New in 2021.2**" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "WriteOnlyObject", + "cweIds": [ + 563 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MethodMayBeStatic", + "shortDescription": { + "text": "Method can be made 'static'" + }, + "fullDescription": { + "text": "Reports methods that can safely be made 'static'. Making methods static when possible can reduce memory consumption and improve your code quality. A method can be 'static' if: it is not 'synchronized', 'native' or 'abstract', does not reference any of non-static methods and non-static fields from the containing class, is not an override and is not overridden in a subclass. Use the following options to configure the inspection: Whether to report only 'private' and 'final' methods, which increases the performance of this inspection. Whether to ignore empty methods. Whether to ignore default methods in interface when using Java 8 or higher. Whether to let the quick-fix replace instance qualifiers with class references in calls to methods which are made 'static', that is, call 'myClass.m()' would be replaced with 'MyClass.m()'.", + "markdown": "Reports methods that can safely be made `static`. Making methods static when possible can reduce memory consumption and improve your code quality.\n\nA method can be `static` if:\n\n* it is not `synchronized`, `native` or `abstract`,\n* does not reference any of non-static methods and non-static fields from the containing class,\n* is not an override and is not overridden in a subclass.\n\nUse the following options to configure the inspection:\n\n* Whether to report only `private` and `final` methods, which increases the performance of this inspection.\n* Whether to ignore empty methods.\n* Whether to ignore default methods in interface when using Java 8 or higher.\n* Whether to let the quick-fix replace instance qualifiers with class references in calls to methods which are made `static`, that is, call `myClass.m()` would be replaced with `MyClass.m()`." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MethodMayBeStatic", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JUnitMixedFramework", + "shortDescription": { + "text": "JUnit API usage from multiple versions in a single TestCase" + }, + "fullDescription": { + "text": "Reports JUnit annotated methods when used in a test case from a different JUnit version. To determine the framework version for a test case the inspection checks the framework version of the super class when available. When a super class is not available it will use the most used framework in the test case. Example (JUnit 4 annotation in JUnit 3 test case): 'public class MyTest extends TestCase {\n @Test\n public void foo() { }\n\n @Test\n @Ignore\n public void testBar() { }\n }' After the quick-fix is applied: 'public class MyTest extends TestCase {\n public void testFoo() {}\n\n public void _testBar() {}\n }' Example (JUnit 5 annotation in JUnit 4 test case): 'public class MyTest {\n @BeforeAll // JUnit 5 lifecycle method\n public void initialize() { }\n\n @org.junit.Test // JUnit 4 test annotation\n public void test() {}\n\n @org.junit.Test // JUnit 4 test annotation\n public void testWouldBeExecuted() {}\n }' After the quick-fix is applied: 'public class MyTest {\n @BeforeClass // JUnit 4 lifecycle method\n public void initialize() { }\n\n @org.junit.Test // JUnit 4 test annotation\n public void test() {}\n\n @org.junit.Test // JUnit 4 test annotation\n public void testWouldBeExecuted() {}\n }'", + "markdown": "Reports JUnit annotated methods when used in a test case from a different JUnit version. To determine the framework version for a test case the inspection checks the framework version of the super class when available. When a super class is not available it will use the most used framework in the test case.\n\nExample (JUnit 4 annotation in JUnit 3 test case):\n\n\n public class MyTest extends TestCase {\n @Test\n public void foo() { }\n\n @Test\n @Ignore\n public void testBar() { }\n }\n\nAfter the quick-fix is applied:\n\n\n public class MyTest extends TestCase {\n public void testFoo() {}\n\n public void _testBar() {}\n }\n\nExample (JUnit 5 annotation in JUnit 4 test case):\n\n\n public class MyTest {\n @BeforeAll // JUnit 5 lifecycle method\n public void initialize() { }\n\n @org.junit.Test // JUnit 4 test annotation\n public void test() {}\n\n @org.junit.Test // JUnit 4 test annotation\n public void testWouldBeExecuted() {}\n }\n\nAfter the quick-fix is applied:\n\n\n public class MyTest {\n @BeforeClass // JUnit 4 lifecycle method\n public void initialize() { }\n\n @org.junit.Test // JUnit 4 test annotation\n public void test() {}\n\n @org.junit.Test // JUnit 4 test annotation\n public void testWouldBeExecuted() {}\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "JUnitMixedFramework", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages/Test frameworks", + "index": 94, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TestMethodWithoutAssertion", + "shortDescription": { + "text": "Test method without assertions" + }, + "fullDescription": { + "text": "Reports test methods that do not contain any assertions. Such methods may indicate either incomplete or weak test cases. Example: 'public class ExtensiveTest {\n\n @Test\n public void testAlive() {\n System.out.println(\"nothing\");\n }\n }' Configure the inspection: Use the table to specify the combinations of fully qualified class name and method name regular expression that should qualify as assertions. Class names also match subclasses. Use the 'assert' keyword is considered an assertion option to specify if the Java 'assert' statements using the 'assert' keyword should be considered an assertion. Use the Ignore test methods which declare exceptions option to ignore the test methods that declare exceptions. This can be useful when you have tests that will throw an exception on failure and thus don't need any assertions.", + "markdown": "Reports test methods that do not contain any assertions. Such methods may indicate either incomplete or weak test cases.\n\n**Example:**\n\n\n public class ExtensiveTest {\n\n @Test\n public void testAlive() {\n System.out.println(\"nothing\");\n }\n }\n\n\nConfigure the inspection:\n\n* Use the table to specify the combinations of fully qualified class name and method name regular expression that should qualify as assertions. Class names also match subclasses.\n* Use the **'assert' keyword is considered an assertion** option to specify if the Java `assert` statements using the `assert` keyword should be considered an assertion.\n* Use the **Ignore test methods which declare exceptions** option to ignore the test methods that declare exceptions. This can be useful when you have tests that will throw an exception on failure and thus don't need any assertions." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "TestMethodWithoutAssertion", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages/Test frameworks", + "index": 94, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SocketResource", + "shortDescription": { + "text": "Socket opened but not safely closed" + }, + "fullDescription": { + "text": "Reports socket resources that are not safely closed. Socket resources reported by this inspection include 'java.net.Socket', 'java.net.DatagramSocket', and 'java.net.ServerSocket'. By default, the inspection assumes that the resources can be closed by any method with 'close' or 'cleanup' in its name. Example: 'byte[] getMessage(ServerSocket socket) throws IOException {\n Socket client = socket.accept(); //socket is not closed\n return client.getInputStream().readAllBytes();\n }' Use the following options to configure the inspection: Whether a socket is allowed to be opened inside a 'try' block. This style is less desirable because it is more verbose than opening a resource in front of a 'try' block. Whether the resource can be closed by any method call with the resource passed as argument.", + "markdown": "Reports socket resources that are not safely closed. Socket resources reported by this inspection include `java.net.Socket`, `java.net.DatagramSocket`, and `java.net.ServerSocket`.\n\n\nBy default, the inspection assumes that the resources can be closed by any method with\n'close' or 'cleanup' in its name.\n\n**Example:**\n\n\n byte[] getMessage(ServerSocket socket) throws IOException {\n Socket client = socket.accept(); //socket is not closed\n return client.getInputStream().readAllBytes();\n }\n\n\nUse the following options to configure the inspection:\n\n* Whether a socket is allowed to be opened inside a `try` block. This style is less desirable because it is more verbose than opening a resource in front of a `try` block.\n* Whether the resource can be closed by any method call with the resource passed as argument." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SocketOpenedButNotSafelyClosed", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Resource management", + "index": 47, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TypeParameterHidesVisibleType", + "shortDescription": { + "text": "Type parameter hides visible type" + }, + "fullDescription": { + "text": "Reports type parameters that have the same names as the visible types in the current scope. Such parameter names may be confusing. Example: 'abstract class MyList extends AbstractList {\n private List elements;\n // type parameter 'T' hides type parameter 'T'\n public T[] toArray(T[] array) {\n return elements.toArray(array);\n }\n}'", + "markdown": "Reports type parameters that have the same names as the visible types in the current scope. Such parameter names may be confusing.\n\nExample:\n\n\n abstract class MyList extends AbstractList {\n private List elements;\n // type parameter 'T' hides type parameter 'T'\n public T[] toArray(T[] array) {\n return elements.toArray(array);\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "TypeParameterHidesVisibleType", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Visibility", + "index": 57, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MaskedAssertion", + "shortDescription": { + "text": "Assertion is suppressed by 'catch'" + }, + "fullDescription": { + "text": "Reports 'assert' statements and test framework assertions that are suppressed by a surrounding catch block. Such assertions will never fail, as the thrown 'AssertionError' will be caught and silently ignored. Example 1: 'void javaAssertion() {\n try {\n ...\n assert 1 == 2;\n } catch (AssertionError e) {\n // the assertion is silently ignored\n }\n }' Example 2: '@Test\n void testWithAssertJ() {\n try {\n ...\n assertThat(1).as(\"test\").isEqualTo(2);\n } catch (AssertionError e) {\n // the assertion is silently ignored\n }\n }' Example 3: '@Test\n void testWithJunit() {\n try {\n ...\n assertEquals(1, 2);\n } catch (AssertionError e) {\n // the assertion is silently ignored\n }\n }' New in 2020.3", + "markdown": "Reports `assert` statements and test framework assertions that are suppressed by a surrounding catch block. Such assertions will never fail, as the thrown `AssertionError` will be caught and silently ignored.\n\n**Example 1:**\n\n\n void javaAssertion() {\n try {\n ...\n assert 1 == 2;\n } catch (AssertionError e) {\n // the assertion is silently ignored\n }\n }\n\n**Example 2:**\n\n\n @Test\n void testWithAssertJ() {\n try {\n ...\n assertThat(1).as(\"test\").isEqualTo(2);\n } catch (AssertionError e) {\n // the assertion is silently ignored\n }\n }\n\n**Example 3:**\n\n\n @Test\n void testWithJunit() {\n try {\n ...\n assertEquals(1, 2);\n } catch (AssertionError e) {\n // the assertion is silently ignored\n }\n }\n\nNew in 2020.3" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "MaskedAssertion", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Test frameworks", + "index": 96, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringTokenizerDelimiter", + "shortDescription": { + "text": "Duplicated delimiters in 'StringTokenizer'" + }, + "fullDescription": { + "text": "Reports 'StringTokenizer()' constructor calls or 'nextToken()' method calls that contain duplicate characters in the delimiter argument. Example: 'void printTokens(String text) {\n StringTokenizer tokenizer = new StringTokenizer(text, \"\\n\\n\");\n while (tokenizer.hasMoreTokens()) {\n System.out.println(tokenizer.nextToken());\n }\n }' After the quick-fix is applied: 'void printTokens(String text) {\n StringTokenizer tokenizer = new StringTokenizer(text, \"\\n\");\n while (tokenizer.hasMoreTokens()) {\n System.out.println(tokenizer.nextToken());\n }\n }'", + "markdown": "Reports `StringTokenizer()` constructor calls or `nextToken()` method calls that contain duplicate characters in the delimiter argument.\n\n**Example:**\n\n\n void printTokens(String text) {\n StringTokenizer tokenizer = new StringTokenizer(text, \"\\n\\n\");\n while (tokenizer.hasMoreTokens()) {\n System.out.println(tokenizer.nextToken());\n }\n }\n\nAfter the quick-fix is applied:\n\n\n void printTokens(String text) {\n StringTokenizer tokenizer = new StringTokenizer(text, \"\\n\");\n while (tokenizer.hasMoreTokens()) {\n System.out.println(tokenizer.nextToken());\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "StringTokenizerDelimiter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReflectionForUnavailableAnnotation", + "shortDescription": { + "text": "Reflective access to a source-only annotation" + }, + "fullDescription": { + "text": "Reports attempts to reflectively check for the presence of a non-runtime annotation. Using 'Class.isAnnotationPresent()' to test for an annotation whose retention policy is set to 'SOURCE' or 'CLASS' (the default) will always have a negative result. This mistake is easy to overlook. Example: '{\n getClass().isAnnotationPresent(SourceAnnotation.class); //always false\n }\n\n @Retention(RetentionPolicy.SOURCE)\n @interface SourceAnnotation {}'", + "markdown": "Reports attempts to reflectively check for the presence of a non-runtime annotation.\n\nUsing `Class.isAnnotationPresent()` to test for an annotation\nwhose retention policy is set to `SOURCE` or `CLASS`\n(the default) will always have a negative result. This mistake is easy to overlook.\n\n**Example:**\n\n\n {\n getClass().isAnnotationPresent(SourceAnnotation.class); //always false\n }\n\n @Retention(RetentionPolicy.SOURCE)\n @interface SourceAnnotation {}\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ReflectionForUnavailableAnnotation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InstantiatingObjectToGetClassObject", + "shortDescription": { + "text": "Instantiating object to get 'Class' object" + }, + "fullDescription": { + "text": "Reports code that instantiates a class to get its class object. It is more performant to access the class object directly by name. Example: 'Class c = new Sample().getClass();' After the quick-fix is applied: 'Class c = Sample.class;'", + "markdown": "Reports code that instantiates a class to get its class object.\n\nIt is more performant to access the class object\ndirectly by name.\n\n**Example:**\n\n\n Class c = new Sample().getClass();\n\nAfter the quick-fix is applied:\n\n\n Class c = Sample.class;\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "InstantiatingObjectToGetClassObject", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ShiftOutOfRange", + "shortDescription": { + "text": "Shift operation by inappropriate constant" + }, + "fullDescription": { + "text": "Reports shift operations where the shift value is a constant outside the reasonable range. Integer shift operations outside the range '0..31' and long shift operations outside the range '0..63' are reported. Shifting by negative or overly large values is almost certainly a coding error. Example: 'int shiftSize = 32;\n // Warning: shift by 32 bits is equivalent to shift by 0 bits, so there's no shift at all.\n int mask = (1 << shiftSize) - 1;'", + "markdown": "Reports shift operations where the shift value is a constant outside the reasonable range.\n\nInteger shift operations outside the range `0..31` and long shift operations outside the\nrange `0..63` are reported. Shifting by negative or overly large values is almost certainly\na coding error.\n\n**Example:**\n\n\n int shiftSize = 32;\n // Warning: shift by 32 bits is equivalent to shift by 0 bits, so there's no shift at all.\n int mask = (1 << shiftSize) - 1;\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ShiftOutOfRange", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Bitwise operation issues", + "index": 97, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassWithMultipleLoggers", + "shortDescription": { + "text": "Class with multiple loggers" + }, + "fullDescription": { + "text": "Reports classes that have multiple loggers declared. Ensuring that every class has a single dedicated logger is an important step in providing a unified logging implementation for an application. For example: 'public class Critical {\n protected static final Logger LOG = Logger.getLogger(Critical.class);\n\n protected static final Logger myLogger = Logger.getLogger(getClass());\n }' Use the table below to specify Logger class names. Classes which declare multiple fields that have the type of one of the specified classes will be reported by this inspection.", + "markdown": "Reports classes that have multiple loggers declared. Ensuring that every class has a single dedicated logger is an important step in providing a unified logging implementation for an application.\n\nFor example:\n\n\n public class Critical {\n protected static final Logger LOG = Logger.getLogger(Critical.class);\n\n protected static final Logger myLogger = Logger.getLogger(getClass());\n }\n\n\nUse the table below to specify Logger class names.\nClasses which declare multiple fields that have the type of one of the specified classes will be reported by this inspection." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassWithMultipleLoggers", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Logging", + "index": 68, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ThreadRun", + "shortDescription": { + "text": "Call to 'Thread.run()'" + }, + "fullDescription": { + "text": "Reports calls to 'run()' on 'java.lang.Thread' or any of its subclasses. While occasionally intended, this is usually a mistake, because 'run()' doesn't start a new thread. To execute the code in a separate thread, 'start()' should be used.", + "markdown": "Reports calls to `run()` on `java.lang.Thread` or any of its subclasses.\n\n\nWhile occasionally intended, this is usually a mistake, because `run()` doesn't start a new thread.\nTo execute the code in a separate thread, `start()` should be used." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "CallToThreadRun", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages", + "index": 2, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EscapedSpace", + "shortDescription": { + "text": "Non-terminal use of '\\s' escape sequence" + }, + "fullDescription": { + "text": "Reports '\\s' escape sequences anywhere except at text-block line endings or within a series of several escaped spaces. Such usages can be confusing or a mistake, especially if the string is interpreted as a regular expression. The '\\s' escape sequence is intended to encode a space at the end of text-block lines where normal spaces are trimmed. In other locations, as well as in regular string or char literals, '\\s' is identical to an ordinary space character ('\" \"'). Example: 'if (str.matches(\"\\s+\")) {...}' Here it's likely that '\"\\\\s+\"' was intended (to match any whitespace character). If not, using 'str.matches(\" +\")' would be less confusing. A quick-fix is provided that replaces '\\s' escapes with space characters. This inspection reports only if the language level of the project or module is 15 or higher. New in 2022.3", + "markdown": "Reports `\\s` escape sequences anywhere except at text-block line endings or within a series of several escaped spaces. Such usages can be confusing or a mistake, especially if the string is interpreted as a regular expression. The `\\s` escape sequence is intended to encode a space at the end of text-block lines where normal spaces are trimmed. In other locations, as well as in regular string or char literals, `\\s` is identical to an ordinary space character (`\" \"`).\n\n**Example:**\n\n\n if (str.matches(\"\\s+\")) {...}\n\nHere it's likely that `\"\\\\s+\"` was intended (to match any whitespace character). If not, using `str.matches(\" +\")` would be less confusing.\n\n\nA quick-fix is provided that replaces `\\s` escapes with space characters.\n\nThis inspection reports only if the language level of the project or module is 15 or higher.\n\nNew in 2022.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EscapedSpace", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AutoBoxing", + "shortDescription": { + "text": "Auto-boxing" + }, + "fullDescription": { + "text": "Reports expressions that are affected by autoboxing conversion (automatic wrapping of primitive values as objects). Try not to use objects instead of primitives. It might significantly affect performance. Example: 'Integer x = 42;' The quick-fix makes the conversion explicit: 'Integer x = Integer.valueOf(42);' AutoBoxing appeared in Java 5. This inspection can help to downgrade for backward compatibility with earlier Java versions.", + "markdown": "Reports expressions that are affected by autoboxing conversion (automatic wrapping of primitive values as objects). Try not to use objects instead of primitives. It might significantly affect performance.\n\n**Example:**\n\n Integer x = 42;\n\nThe quick-fix makes the conversion explicit:\n\n Integer x = Integer.valueOf(42);\n\n\n*AutoBoxing* appeared in Java 5.\nThis inspection can help to downgrade for backward compatibility with earlier Java versions." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AutoBoxing", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ExtendsThrowable", + "shortDescription": { + "text": "Class directly extends 'Throwable'" + }, + "fullDescription": { + "text": "Reports classes that directly extend 'java.lang.Throwable'. Extending 'java.lang.Throwable' directly is generally considered bad practice. It is usually enough to extend 'java.lang.RuntimeException', 'java.lang.Exception', or - in special cases - 'java.lang.Error'. Example: 'class EnigmaThrowable extends Throwable {} // warning: Class 'EnigmaThrowable' directly extends 'java.lang.Throwable''", + "markdown": "Reports classes that directly extend `java.lang.Throwable`.\n\nExtending `java.lang.Throwable` directly is generally considered bad practice.\nIt is usually enough to extend `java.lang.RuntimeException`, `java.lang.Exception`, or - in special\ncases - `java.lang.Error`.\n\n**Example:**\n\n\n class EnigmaThrowable extends Throwable {} // warning: Class 'EnigmaThrowable' directly extends 'java.lang.Throwable'\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ExtendsThrowable", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InterfaceNeverImplemented", + "shortDescription": { + "text": "Interface which has no concrete subclass" + }, + "fullDescription": { + "text": "Reports interfaces that have no concrete subclasses. Configure the inspection: Use the list below to add annotations. Interfaces declared with one of these annotations will be ignored by the inspection. Use the checkbox below to ignore interfaces that only declare constant fields. Such interfaces may still be usable even without implementations.", + "markdown": "Reports interfaces that have no concrete subclasses.\n\nConfigure the inspection:\n\n* Use the list below to add annotations. Interfaces declared with one of these annotations will be ignored by the inspection.\n* Use the checkbox below to ignore interfaces that only declare constant fields. Such interfaces may still be usable even without implementations." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "InterfaceNeverImplemented", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Inheritance issues", + "index": 25, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ContinueStatement", + "shortDescription": { + "text": "'continue' statement" + }, + "fullDescription": { + "text": "Reports 'continue' statements. 'continue' statements complicate refactoring and can be confusing. Example: 'void foo(List strs) {\n for (String str : strs) {\n if (str.contains(\"skip\")) continue;\n handleStr(str);\n }\n }'", + "markdown": "Reports `continue` statements.\n\n`continue` statements complicate refactoring and can be confusing.\n\nExample:\n\n\n void foo(List strs) {\n for (String str : strs) {\n if (str.contains(\"skip\")) continue;\n handleStr(str);\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ContinueStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ThreadDeathRethrown", + "shortDescription": { + "text": "'ThreadDeath' not rethrown" + }, + "fullDescription": { + "text": "Reports 'try' statements that catch 'java.lang.ThreadDeath' and do not rethrow the exception. Example: 'try {\n executeInParallel(request);\n } catch (ThreadDeath ex) { // warning: ThreadDeath 'ex' not rethrown\n return false;\n }'", + "markdown": "Reports `try` statements that catch `java.lang.ThreadDeath` and do not rethrow the exception.\n\n**Example:**\n\n\n try {\n executeInParallel(request);\n } catch (ThreadDeath ex) { // warning: ThreadDeath 'ex' not rethrown\n return false;\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ThreadDeathNotRethrown", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MisorderedAssertEqualsArguments", + "shortDescription": { + "text": "Misordered 'assertEquals()' arguments" + }, + "fullDescription": { + "text": "Reports calls to 'assertEquals()' that have the expected argument and the actual argument in the wrong order. For JUnit 3, 4, and 5 the correct order is '(expected, actual)'. For TestNG the correct order is '(actual, expected)'. Such calls will behave fine for assertions that pass, but may give confusing error reports on failure. Use the quick-fix to flip the order of the arguments. Example (JUnit): 'assertEquals(actual, expected)' After the quick-fix is applied: 'assertEquals(expected, actual)'", + "markdown": "Reports calls to `assertEquals()` that have the expected argument and the actual argument in the wrong order.\n\n\nFor JUnit 3, 4, and 5 the correct order is `(expected, actual)`.\nFor TestNG the correct order is `(actual, expected)`.\n\n\nSuch calls will behave fine for assertions that pass, but may give confusing error reports on failure.\nUse the quick-fix to flip the order of the arguments.\n\n**Example (JUnit):**\n\n\n assertEquals(actual, expected)\n\nAfter the quick-fix is applied:\n\n\n assertEquals(expected, actual)\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MisorderedAssertEqualsArguments", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Test frameworks", + "index": 96, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StaticVariableInitialization", + "shortDescription": { + "text": "Static field may not be initialized" + }, + "fullDescription": { + "text": "Reports 'static' variables that may be uninitialized upon class initialization. Example: 'class Foo {\n public static int bar;\n\n static { }\n }' Note that this inspection uses a very conservative dataflow algorithm and may incorrectly report 'static' variables as uninitialized. Variables reported as initialized will always be initialized. Use the Ignore primitive fields option to ignore uninitialized primitive fields.", + "markdown": "Reports `static` variables that may be uninitialized upon class initialization.\n\n**Example:**\n\n\n class Foo {\n public static int bar;\n\n static { }\n }\n\nNote that this inspection uses a very conservative dataflow algorithm and may incorrectly report `static` variables as uninitialized. Variables\nreported as initialized will always be initialized.\n\nUse the **Ignore primitive fields** option to ignore uninitialized primitive fields." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StaticVariableMayNotBeInitialized", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Initialization", + "index": 28, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConstantAssertCondition", + "shortDescription": { + "text": "Constant condition in 'assert' statement" + }, + "fullDescription": { + "text": "Reports 'assert' statement conditions that are constants. 'assert' statements with constant conditions will either always fail or always succeed. Such statements might be left over after a refactoring and are probably not intended. Example: 'void foo() {\n assert true;\n }'", + "markdown": "Reports `assert` statement conditions that are constants. `assert` statements with constant conditions will either always fail or always succeed. Such statements might be left over after a refactoring and are probably not intended.\n\n**Example:**\n\n\n void foo() {\n assert true;\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ConstantAssertCondition", + "cweIds": [ + 570, + 571 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JavaReflectionInvocation", + "shortDescription": { + "text": "Reflective invocation arguments mismatch" + }, + "fullDescription": { + "text": "Reports cases in which the arguments provided to 'Method.invoke()' and 'Constructor.newInstance()' do not match the signature specified in 'Class.getMethod()' and 'Class.getConstructor()'. Example: 'Method m = myObj.getClass().getMethod(\"myMethod\", int.class);\n // the argument should be an int value\n m.invoke(myObj, \"abc\");' New in 2017.2", + "markdown": "Reports cases in which the arguments provided to `Method.invoke()` and `Constructor.newInstance()` do not match the signature specified in `Class.getMethod()` and `Class.getConstructor()`.\n\nExample:\n\n\n Method m = myObj.getClass().getMethod(\"myMethod\", int.class);\n // the argument should be an **int** value\n m.invoke(myObj, \"abc\");\n\nNew in 2017.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "JavaReflectionInvocation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Reflective access", + "index": 98, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CaughtExceptionImmediatelyRethrown", + "shortDescription": { + "text": "Caught exception is immediately rethrown" + }, + "fullDescription": { + "text": "Reports 'catch' blocks that immediately rethrow the caught exception without performing any action on it. Such 'catch' blocks are unnecessary and have no error handling. Example: 'try {\n new FileInputStream(\"\");\n } catch (FileNotFoundException e) {\n throw e;\n }'", + "markdown": "Reports `catch` blocks that immediately rethrow the caught exception without performing any action on it. Such `catch` blocks are unnecessary and have no error handling.\n\n**Example:**\n\n\n try {\n new FileInputStream(\"\");\n } catch (FileNotFoundException e) {\n throw e;\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "CaughtExceptionImmediatelyRethrown", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CloneCallsConstructors", + "shortDescription": { + "text": "'clone()' instantiates objects with constructor" + }, + "fullDescription": { + "text": "Reports calls to object constructors inside 'clone()' methods. It is considered good practice to call 'clone()' to instantiate objects inside of a 'clone()' method instead of creating them directly to support later subclassing. This inspection will not report 'clone()' methods declared as 'final' or 'clone()' methods on 'final' classes.", + "markdown": "Reports calls to object constructors inside `clone()` methods.\n\nIt is considered good practice to call `clone()` to instantiate objects inside of a `clone()` method\ninstead of creating them directly to support later subclassing.\nThis inspection will not report\n`clone()` methods declared as `final`\nor `clone()` methods on `final` classes." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CloneCallsConstructors", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Cloning issues", + "index": 82, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryModuleDependencyInspection", + "shortDescription": { + "text": "Unnecessary module dependency" + }, + "fullDescription": { + "text": "Reports dependencies on modules that are not used. The quick-fix safely removes such unused dependencies.", + "markdown": "Reports dependencies on modules that are not used. The quick-fix safely removes such unused dependencies." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryModuleDependencyInspection", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ArrayHashCode", + "shortDescription": { + "text": "'hashCode()' called on array" + }, + "fullDescription": { + "text": "Reports incorrect hash code calculation for arrays. In order to correctly calculate the hash code for an array, use: 'Arrays.hashcode()' for linear arrays 'Arrays.deepHashcode()' for multidimensional arrays These methods should also be used with 'Objects.hash()' when the sequence of input values includes arrays, for example: 'Objects.hash(string, Arrays.hashcode(array))'", + "markdown": "Reports incorrect hash code calculation for arrays.\n\nIn order to\ncorrectly calculate the hash code for an array, use:\n\n* `Arrays.hashcode()` for linear arrays\n* `Arrays.deepHashcode()` for multidimensional arrays\n\nThese methods should also be used with `Objects.hash()` when the sequence of input values includes arrays, for example: `Objects.hash(string, Arrays.hashcode(array))`" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ArrayHashCode", + "cweIds": [ + 328 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ArraysAsListWithZeroOrOneArgument", + "shortDescription": { + "text": "Call to 'Arrays.asList()' with too few arguments" + }, + "fullDescription": { + "text": "Reports calls to 'Arrays.asList()' with at most one argument. Such calls could be replaced with 'Collections.singletonList()', 'Collections.emptyList()', or 'List.of()' on JDK 9 and later, which will save some memory. In particular, 'Collections.emptyList()' and 'List.of()' with no arguments always return a shared instance, while 'Arrays.asList()' with no arguments creates a new object every time it's called. Note: the lists returned by 'Collections.singletonList()' and 'List.of()' are immutable, while the list returned 'Arrays.asList()' allows calling the 'set()' method. This may break the code in rare cases. Example: 'List empty = Arrays.asList();\n List one = Arrays.asList(\"one\");' After the quick-fix is applied: 'List empty = Collections.emptyList();\n List one = Collections.singletonList(\"one\");'", + "markdown": "Reports calls to `Arrays.asList()` with at most one argument.\n\n\nSuch calls could be replaced\nwith `Collections.singletonList()`, `Collections.emptyList()`,\nor `List.of()` on JDK 9 and later, which will save some memory.\n\nIn particular, `Collections.emptyList()` and `List.of()` with no arguments\nalways return a shared instance,\nwhile `Arrays.asList()` with no arguments creates a new object every time it's called.\n\nNote: the lists returned by `Collections.singletonList()` and `List.of()` are immutable,\nwhile the list returned `Arrays.asList()` allows calling the `set()` method.\nThis may break the code in rare cases.\n\n**Example:**\n\n\n List empty = Arrays.asList();\n List one = Arrays.asList(\"one\");\n\nAfter the quick-fix is applied:\n\n\n List empty = Collections.emptyList();\n List one = Collections.singletonList(\"one\");\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ArraysAsListWithZeroOrOneArgument", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "WaitNotInLoop", + "shortDescription": { + "text": "'wait()' not called in loop" + }, + "fullDescription": { + "text": "Reports calls to 'wait()' that are not made inside a loop. 'wait()' is normally used to suspend a thread until some condition becomes true. As the thread could have been waken up for a different reason, the condition should be checked after the 'wait()' call returns. A loop is a simple way to achieve this. Example: 'class BoundedCounter {\n private int count;\n synchronized void inc() throws InterruptedException {\n if (count >= 10) wait();\n ++count;\n }\n }' Good code should look like this: 'class BoundedCounter {\n private int count;\n synchronized void inc() throws InterruptedException {\n while (count >= 10) wait();\n ++count;\n }\n }'", + "markdown": "Reports calls to `wait()` that are not made inside a loop.\n\n\n`wait()` is normally used to suspend a thread until some condition becomes true.\nAs the thread could have been waken up for a different reason,\nthe condition should be checked after the `wait()` call returns.\nA loop is a simple way to achieve this.\n\n**Example:**\n\n\n class BoundedCounter {\n private int count;\n synchronized void inc() throws InterruptedException {\n if (count >= 10) wait();\n ++count;\n }\n }\n\nGood code should look like this:\n\n\n class BoundedCounter {\n private int count;\n synchronized void inc() throws InterruptedException {\n while (count >= 10) wait();\n ++count;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "WaitNotInLoop", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ExternalizableWithSerializationMethods", + "shortDescription": { + "text": "Externalizable class with 'readObject()' or 'writeObject()'" + }, + "fullDescription": { + "text": "Reports 'Externalizable' classes that define 'readObject()' or 'writeObject()' methods. These methods are not called for serialization of 'Externalizable' objects. Example: 'abstract class Crucial implements Externalizable {\n int value;\n private void readObject(ObjectInputStream in) {\n value = in.readInt();\n }\n }'", + "markdown": "Reports `Externalizable` classes that define `readObject()` or `writeObject()` methods. These methods are not called for serialization of `Externalizable` objects.\n\n**Example:**\n\n\n abstract class Crucial implements Externalizable {\n int value;\n private void readObject(ObjectInputStream in) {\n value = in.readInt();\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ExternalizableClassWithSerializationMethods", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnstableApiUsage", + "shortDescription": { + "text": "Unstable API Usage" + }, + "fullDescription": { + "text": "Reports usages of an API marked with one of the annotations as unstable. Such an API may be changed or removed in future versions, breaking the code that uses it. The annotations which are used to mark unstable APIs are shown in the list below. By default, the inspection ignores usages of unstable APIs if their declarations are located in sources of the same project. In such cases it'll be possible to update the usages when you change APIs. However, it may be inconvenient if the project is big, so one can switch off the Ignore API declared in this project option to report the usages of unstable APIs declared in both the project sources and libraries.", + "markdown": "Reports usages of an API marked with one of the annotations as unstable. Such an API may be changed or removed in future versions, breaking the code that uses it.\n\nThe annotations which are used to mark unstable APIs are shown in the list below.\n\nBy default, the inspection ignores usages of unstable APIs\nif their declarations are located in sources of the same project. In such cases it'll be possible to update the usages when you change APIs.\nHowever, it may be inconvenient if the project is big, so one can switch off the **Ignore API declared in this project** option to report\nthe usages of unstable APIs declared in both the project sources and libraries." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "UnstableApiUsage", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages", + "index": 2, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SafeLock", + "shortDescription": { + "text": "Lock acquired but not safely unlocked" + }, + "fullDescription": { + "text": "Reports 'java.util.concurrent.locks.Lock' resources that are not acquired in front of a 'try' block or not unlocked in the corresponding 'finally' block. Such resources may be inadvertently leaked if an exception is thrown before the resource is closed. Example: 'lock.lock(); // will be reported since the 'finally' block is missing\n try {\n doSmthWithLock();\n } catch (IOException e) {\n throw new UncheckedIOException(e);\n }\n lock.unlock();'", + "markdown": "Reports `java.util.concurrent.locks.Lock` resources that are not acquired in front of a `try` block or not unlocked in the corresponding `finally` block. Such resources may be inadvertently leaked if an exception is thrown before the resource is closed.\n\n**Example:**\n\n\n lock.lock(); // will be reported since the 'finally' block is missing\n try {\n doSmthWithLock();\n } catch (IOException e) {\n throw new UncheckedIOException(e);\n }\n lock.unlock();\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LockAcquiredButNotSafelyReleased", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LambdaUnfriendlyMethodOverload", + "shortDescription": { + "text": "Lambda-unfriendly method overload" + }, + "fullDescription": { + "text": "Reports overloaded methods that take functional interfaces with conflicting abstract method signatures. Such overloads introduce ambiguity and require callers to cast lambdas to a specific type or specify lambda parameter types explicitly. It is preferable to give the overloaded methods different names to eliminate ambiguity. Example: 'interface MyExecutor {\n void execute(Supplier supplier);\n void execute(Callable callable);\n }' Here, 'Supplier' and 'Callable' are functional interfaces whose single abstract methods do not take any parameters and return a non-void value. As a result, the type of the lambda cannot be inferred at the call site unless an explicit cast is used.", + "markdown": "Reports overloaded methods that take functional interfaces with conflicting abstract method signatures.\n\nSuch overloads introduce ambiguity and require callers to cast lambdas to a specific type or specify lambda parameter types explicitly.\nIt is preferable to give the overloaded methods different names to eliminate ambiguity.\n\nExample:\n\n\n interface MyExecutor {\n void execute(Supplier supplier);\n void execute(Callable callable);\n }\n\n\nHere, `Supplier` and `Callable` are functional interfaces\nwhose single abstract methods do not take any parameters and return a non-void value.\nAs a result, the type of the lambda cannot be inferred at the call site unless an explicit cast is used." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LambdaUnfriendlyMethodOverload", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions/Method", + "index": 88, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JavaLangInvokeHandleSignature", + "shortDescription": { + "text": "MethodHandle/VarHandle type mismatch" + }, + "fullDescription": { + "text": "Reports 'MethodHandle' and 'VarHandle' factory method calls that don't match any method or field. Also reports arguments to 'MethodHandle.invoke()' and similar methods, that don't match the 'MethodHandle' signature and arguments to 'VarHandle.set()' that don't match the 'VarHandle' type. Examples: MethodHandle mh = MethodHandles.lookup().findVirtual(\n MyClass.class, \"foo\", MethodType.methodType(void.class, int.class));\n // the argument should be an int value\n mh.invoke(myObj, \"abc\");\n // the argument should be String.class\n VarHandle vh = MethodHandles.lookup().findVarHandle(\n MyClass.class, \"text\", int.class);\n VarHandle vh = MethodHandles.lookup().findVarHandle(\n MyClass.class, \"text\", String.class);\n // the argument should be a String value\n vh.set(myObj, 42);\n New in 2017.2", + "markdown": "Reports `MethodHandle` and `VarHandle` factory method calls that don't match any method or field.\n\nAlso reports arguments to `MethodHandle.invoke()` and similar methods, that don't match the `MethodHandle` signature\nand arguments to `VarHandle.set()` that don't match the `VarHandle` type.\n\n\nExamples:\n\n```\n MethodHandle mh = MethodHandles.lookup().findVirtual(\n MyClass.class, \"foo\", MethodType.methodType(void.class, int.class));\n // the argument should be an int value\n mh.invoke(myObj, \"abc\");\n```\n\n```\n // the argument should be String.class\n VarHandle vh = MethodHandles.lookup().findVarHandle(\n MyClass.class, \"text\", int.class);\n```\n\n```\n VarHandle vh = MethodHandles.lookup().findVarHandle(\n MyClass.class, \"text\", String.class);\n // the argument should be a String value\n vh.set(myObj, 42);\n```\n\n\nNew in 2017.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "JavaLangInvokeHandleSignature", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Reflective access", + "index": 98, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CanBeFinal", + "shortDescription": { + "text": "Declaration can have 'final' modifier" + }, + "fullDescription": { + "text": "Reports fields, methods, or classes that may have the 'final' modifier added to their declarations. Final classes can't be extended, final methods can't be overridden, and final fields can't be reassigned. Example: 'public class Person {\n private String name;\n\n Person(String name) {\n this.name = name;\n }\n\n public String getName() {\n return name;\n }\n\n public String toString() {\n return getName();\n }\n }' After the quick-fix is applied: 'public final class Person {\n private final String name;\n\n Person(String name) {\n this.name = name;\n }\n\n public final String getName() {\n return name;\n }\n\n public final String toString() {\n return getName();\n }\n }' Use the Report classes and Report methods options to define which declarations are to be reported.", + "markdown": "Reports fields, methods, or classes that may have the `final` modifier added to their declarations.\n\nFinal classes can't be extended, final methods can't be overridden, and final fields can't be reassigned.\n\n**Example:**\n\n\n public class Person {\n private String name;\n\n Person(String name) {\n this.name = name;\n }\n\n public String getName() {\n return name;\n }\n\n public String toString() {\n return getName();\n }\n }\n\nAfter the quick-fix is applied:\n\n\n public final class Person {\n private final String name;\n\n Person(String name) {\n this.name = name;\n }\n\n public final String getName() {\n return name;\n }\n\n public final String toString() {\n return getName();\n }\n }\n\nUse the **Report classes** and **Report methods** options to define which declarations are to be reported." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CanBeFinal", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReturnThis", + "shortDescription": { + "text": "Return of 'this'" + }, + "fullDescription": { + "text": "Reports methods returning 'this'. While such a return is valid, it is rarely necessary, and usually indicates that the method is intended to be used as part of a chain of similar method calls (for example, 'buffer.append(\"foo\").append(\"bar\").append(\"baz\")'). Such chains are frowned upon by many coding standards. Example: 'public Builder append(String str) {\n // [...]\n return this;\n }'", + "markdown": "Reports methods returning `this`.\n\n\nWhile such a return is valid, it is rarely necessary, and usually indicates that the method is intended to be used\nas part of a chain of similar method calls (for example, `buffer.append(\"foo\").append(\"bar\").append(\"baz\")`).\nSuch chains are frowned upon by many coding standards.\n\n**Example:**\n\n\n public Builder append(String str) {\n // [...]\n return this;\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ReturnOfThis", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryLabelOnBreakStatement", + "shortDescription": { + "text": "Unnecessary label on 'break' statement" + }, + "fullDescription": { + "text": "Reports 'break' statements with unnecessary labels. Such labels do not change the control flow but make the code difficult to follow. Example: 'label:\n for(int i = 0; i < 10; i++) {\n if (shouldBreak()) break label;\n //doSmth\n }' After the quick-fix is applied: 'label:\n for(int i = 0; i < 10; i++) {\n if (shouldBreak()) break;\n //doSmth\n }'", + "markdown": "Reports `break` statements with unnecessary labels. Such labels do not change the control flow but make the code difficult to follow.\n\n**Example:**\n\n\n label:\n for(int i = 0; i < 10; i++) {\n if (shouldBreak()) break label;\n //doSmth\n }\n\nAfter the quick-fix is applied:\n\n\n label:\n for(int i = 0; i < 10; i++) {\n if (shouldBreak()) break;\n //doSmth\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryLabelOnBreakStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SerializableRecordContainsIgnoredMembers", + "shortDescription": { + "text": "'record' contains ignored members" + }, + "fullDescription": { + "text": "Reports serialization methods or fields defined in a 'record' class. Serialization methods include 'writeObject()', 'readObject()', 'readObjectNoData()', 'writeExternal()', and 'readExternal()' and the field 'serialPersistentFields'. These members are not used for the serialization or deserialization of records and therefore unnecessary. Examples: 'record R1() implements Serializable {\n // The field is ignored during record serialization\n @Serial\n private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];\n\n // The method is ignored during record serialization\n @Serial\n private void writeObject(ObjectOutputStream out) throws IOException {\n }\n }' 'record R2() implements Externalizable {\n // The method is ignored during record serialization\n @Override\n public void writeExternal(ObjectOutput out) throws IOException {\n }\n\n // The method is ignored during record serialization\n @Override\n public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {\n }\n }' This inspection only reports if the language level of the project or module is 14 or higher. New in 2020.3", + "markdown": "Reports serialization methods or fields defined in a `record` class. Serialization methods include `writeObject()`, `readObject()`, `readObjectNoData()`, `writeExternal()`, and `readExternal()` and the field `serialPersistentFields`. These members are not used for the serialization or deserialization of records and therefore unnecessary.\n\n**Examples:**\n\n\n record R1() implements Serializable {\n // The field is ignored during record serialization\n @Serial\n private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];\n\n // The method is ignored during record serialization\n @Serial\n private void writeObject(ObjectOutputStream out) throws IOException {\n }\n }\n\n\n record R2() implements Externalizable {\n // The method is ignored during record serialization\n @Override\n public void writeExternal(ObjectOutput out) throws IOException {\n }\n\n // The method is ignored during record serialization\n @Override\n public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {\n }\n }\n\nThis inspection only reports if the language level of the project or module is 14 or higher.\n\nNew in 2020.3" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SerializableRecordContainsIgnoredMembers", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NakedNotify", + "shortDescription": { + "text": "'notify()' or 'notifyAll()' without corresponding state change" + }, + "fullDescription": { + "text": "Reports 'Object.notify()' or 'Object.notifyAll()' being called without any detectable state change occurring. Normally, 'Object.notify()' and 'Object.notifyAll()' are used to inform other threads that a state change has occurred. That state change should occur in a synchronized context that contains the 'Object.notify()' or 'Object.notifyAll()' call, and prior to the call. While not having such a state change isn't necessarily incorrect, it is certainly worth examining. Example: 'synchronized (this) {\n notify();\n }\n // no state change\n synchronized (this) {\n notify(); // this notify might be redundant\n }'", + "markdown": "Reports `Object.notify()` or `Object.notifyAll()` being called without any detectable state change occurring.\n\n\nNormally, `Object.notify()` and `Object.notifyAll()` are used to inform other threads that a state change has\noccurred. That state change should occur in a synchronized context that contains the `Object.notify()` or\n`Object.notifyAll()` call, and prior to the call. While not having such a state change isn't necessarily incorrect, it is\ncertainly worth examining.\n\n**Example:**\n\n\n synchronized (this) {\n notify();\n }\n // no state change\n synchronized (this) {\n notify(); // this notify might be redundant\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NakedNotify", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassCoupling", + "shortDescription": { + "text": "Overly coupled class" + }, + "fullDescription": { + "text": "Reports classes that reference too many other classes. Classes with too high coupling can be very fragile, and should probably be split into smaller classes. Configure the inspection: Use the Class coupling limit field to specify the maximum allowed coupling for a class. Use the Include couplings to java system classes option to specify whether references to system classes (those in the 'java.'or 'javax.' packages) should be counted. Use the Include couplings to library classes option to specify whether references to any library classes should be counted.", + "markdown": "Reports classes that reference too many other classes.\n\nClasses with too high coupling can be very fragile, and should probably be split into smaller classes.\n\nConfigure the inspection:\n\n* Use the **Class coupling limit** field to specify the maximum allowed coupling for a class.\n* Use the **Include couplings to java system classes** option to specify whether references to system classes (those in the `java.`or `javax.` packages) should be counted.\n* Use the **Include couplings to library classes** option to specify whether references to any library classes should be counted." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OverlyCoupledClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class metrics", + "index": 87, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessarilyQualifiedInnerClassAccess", + "shortDescription": { + "text": "Unnecessarily qualified inner class access" + }, + "fullDescription": { + "text": "Reports any references to inner classes that are unnecessarily qualified with the name of the enclosing class. Such a qualification can be safely removed, which sometimes adds an import for the inner class. Example: 'class X {\n X.Y foo;\n class Y{}\n }' After the quick-fix is applied: 'class X {\n Y foo;\n class Y{}\n }' Use the Ignore references for which an import is needed option to ignore references to inner classes, where removing the qualification adds an import.", + "markdown": "Reports any references to inner classes that are unnecessarily qualified with the name of the enclosing class.\n\nSuch a qualification can be safely removed, which sometimes adds an import for the inner class.\n\nExample:\n\n\n class X {\n X.Y foo;\n class Y{}\n }\n\nAfter the quick-fix is applied:\n\n\n class X {\n Y foo;\n class Y{}\n }\n\nUse the **Ignore references for which an import is needed** option to ignore references to inner classes, where\nremoving the qualification adds an import." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "UnnecessarilyQualifiedInnerClassAccess", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EmptySynchronizedStatement", + "shortDescription": { + "text": "Empty 'synchronized' statement" + }, + "fullDescription": { + "text": "Reports 'synchronized' statements with empty bodies. Empty 'synchronized' statements are sometimes used to wait for other threads to release a particular resource. However, there is no guarantee that the same resource won't be acquired again right after the empty 'synchronized' statement finishes. For proper synchronization, the resource should be utilized inside the 'synchronized' block. Also, an empty 'synchronized' block may appear after a refactoring when redundant code was removed. In this case, the 'synchronized' block itself will be redundant and should be removed as well. Example: 'synchronized(lock) {}' A quick-fix is suggested to remove the empty synchronized statement. This inspection is disabled in JSP files.", + "markdown": "Reports `synchronized` statements with empty bodies.\n\n\nEmpty `synchronized` statements are sometimes used to wait for other threads to\nrelease a particular resource. However, there is no guarantee that the same resource\nwon't be acquired again right after the empty `synchronized` statement finishes.\nFor proper synchronization, the resource should be utilized inside the `synchronized` block.\n\n\nAlso, an empty `synchronized` block may appear after a refactoring\nwhen redundant code was removed. In this case, the `synchronized` block\nitself will be redundant and should be removed as well.\n\nExample:\n\n\n synchronized(lock) {}\n\n\nA quick-fix is suggested to remove the empty synchronized statement.\n\n\nThis inspection is disabled in JSP files." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "EmptySynchronizedStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TextBlockMigration", + "shortDescription": { + "text": "Text block can be used" + }, + "fullDescription": { + "text": "Reports 'String' concatenations that can be simplified by replacing them with text blocks. Requirements: '\\n' occurs two or more times. Text blocks are not concatenated. Use the Apply to single string literals option to suggest the fix for single literals containing line breaks. Example: 'String html = \"\\n\" +\n \" \\n\" +\n \"

Hello, world

\\n\" +\n \" \\n\" +\n \"\\n\";' After the quick-fix is applied: 'String html = \"\"\"\n \n \n

Hello, world

\n \n \n \"\"\";' This inspection only reports if the language level of the project or module is 15 or higher. New in 2019.3", + "markdown": "Reports `String` concatenations that can be simplified by replacing them with text blocks.\n\nRequirements:\n\n* `\\n` occurs two or more times.\n* Text blocks are not concatenated.\n\n\nUse the **Apply to single string literals** option to suggest the fix for single literals containing line breaks.\n\n\n**Example:**\n\n\n String html = \"\\n\" +\n \" \\n\" +\n \"

Hello, world

\\n\" +\n \" \\n\" +\n \"\\n\";\n\nAfter the quick-fix is applied:\n\n\n String html = \"\"\"\n \n \n

Hello, world

\n \n \n \"\"\";\n\nThis inspection only reports if the language level of the project or module is 15 or higher.\n\nNew in 2019.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "TextBlockMigration", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 15", + "index": 99, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ObjectAllocationInLoop", + "shortDescription": { + "text": "Object allocation in loop" + }, + "fullDescription": { + "text": "Reports object or array allocations inside loops. While not necessarily a problem, an object allocation inside a loop is a great place to look for memory leaks and performance issues. The inspection reports the following constructs: Explicit allocations via 'new' operator Methods known to return new object Instance-bound method references Lambdas that capture variables or 'this' reference Example: '// Explicit allocation\n for (Status status : Status.values()) {\n declarationsMap.put(status, new ArrayList<>());\n }\n\n // Lambda captures variable\n String message = \"Engine running.\";\n for (Engine engine : engines) {\n if (!isRunning(engine)) {\n logger.warn(() -> {\n return String.format(message);\n });\n }\n }\n\n // Instance-bound method reference\n for(Node node : nodes) {\n descriptor = node.getDescription();\n descriptor.ifPresent(dynamicTestExecutor::execute);\n }'", + "markdown": "Reports object or array allocations inside loops. While not necessarily a problem, an object allocation inside a loop is a great place to look for memory leaks and performance issues.\n\n\nThe inspection reports the following constructs:\n\n* Explicit allocations via `new` operator\n* Methods known to return new object\n* Instance-bound method references\n* Lambdas that capture variables or `this` reference\n\n**Example:**\n\n\n // Explicit allocation\n for (Status status : Status.values()) {\n declarationsMap.put(status, new ArrayList<>());\n }\n\n // Lambda captures variable\n String message = \"Engine running.\";\n for (Engine engine : engines) {\n if (!isRunning(engine)) {\n logger.warn(() -> {\n return String.format(message);\n });\n }\n }\n\n // Instance-bound method reference\n for(Node node : nodes) {\n descriptor = node.getDescription();\n descriptor.ifPresent(dynamicTestExecutor::execute);\n }\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ObjectAllocationInLoop", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JavadocLinkAsPlainText", + "shortDescription": { + "text": "Link specified as plain text" + }, + "fullDescription": { + "text": "Reports plain text links in Javadoc comments. The quick-fix suggests to wrap the link in an '' tag. Example: 'class Main {\n /**\n * https://en.wikipedia.org/\n */\n void foo() {}\n }' After the quick-fix is applied: 'class Main {\n /**\n * https://en.wikipedia.org/\n */\n void foo() {}\n }' New in 2022.1", + "markdown": "Reports plain text links in Javadoc comments.\n\n\nThe quick-fix suggests to wrap the link in an `` tag.\n\n**Example:**\n\n\n class Main {\n /**\n * https://en.wikipedia.org/\n */\n void foo() {}\n }\n\nAfter the quick-fix is applied:\n\n\n class Main {\n /**\n * https://en.wikipedia.org/\n */\n void foo() {}\n }\n\nNew in 2022.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "JavadocLinkAsPlainText", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Javadoc", + "index": 45, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SharedThreadLocalRandom", + "shortDescription": { + "text": "'ThreadLocalRandom' instance might be shared" + }, + "fullDescription": { + "text": "Reports 'java.util.concurrent.ThreadLocalRandom' instances which might be shared between threads. A 'ThreadLocalRandom' should not be shared between threads because that is not thread-safe. The inspection reports instances that are assigned to a field used as a method argument, or assigned to a local variable and used in anonymous or nested classes as they might get shared between threads. Usages of 'ThreadLocalRandom' should typically look like 'ThreadLocalRandom.current().nextInt(...)' (or 'nextDouble(...)' etc.). When all usages are in this form, 'ThreadLocalRandom' instances cannot be used accidentally by multiple threads. Example: 'class Main {\n void printRandomNumbersAsync() {\n ThreadLocalRandom random = ThreadLocalRandom.current();\n CompletableFuture.supplyAsync(() -> generateNumbers(random))\n .thenAccept(numbers -> System.out.println(Arrays.toString(numbers)));\n }\n\n private int[] generateNumbers(Random random) {\n return random.ints(1000, 0, 100).toArray();\n }\n }' Use the options to list methods that are safe to be passed to 'ThreadLocalRandom' instances as an argument. It's possible to use regular expressions for method names.", + "markdown": "Reports `java.util.concurrent.ThreadLocalRandom` instances which might be shared between threads.\n\n\nA `ThreadLocalRandom` should not be shared between threads because that is not thread-safe.\nThe inspection reports instances that are assigned to a field used as a method argument,\nor assigned to a local variable and used in anonymous or nested classes as they might get shared between threads.\n\n\nUsages of `ThreadLocalRandom` should typically look like `ThreadLocalRandom.current().nextInt(...)`\n(or `nextDouble(...)` etc.).\nWhen all usages are in this form, `ThreadLocalRandom` instances cannot be used accidentally by multiple threads.\n\n**Example:**\n\n\n class Main {\n void printRandomNumbersAsync() {\n ThreadLocalRandom random = ThreadLocalRandom.current();\n CompletableFuture.supplyAsync(() -> generateNumbers(random))\n .thenAccept(numbers -> System.out.println(Arrays.toString(numbers)));\n }\n\n private int[] generateNumbers(Random random) {\n return random.ints(1000, 0, 100).toArray();\n }\n }\n \n\nUse the options to list methods that are safe to be passed to `ThreadLocalRandom` instances as an argument.\nIt's possible to use regular expressions for method names." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SharedThreadLocalRandom", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonFinalClone", + "shortDescription": { + "text": "Non-final 'clone()' in secure context" + }, + "fullDescription": { + "text": "Reports 'clone()' methods without the 'final' modifier. Since 'clone()' can be used to instantiate objects without using a constructor, allowing the 'clone()' method to be overridden may result in corrupted objects, and even in security exploits. This may be prevented by making the 'clone()' method or the enclosing class itself 'final'. Example: 'class Main implements Cloneable {\n @Override\n protected Object clone() throws CloneNotSupportedException {\n return super.clone();\n }\n }'", + "markdown": "Reports `clone()` methods without the `final` modifier.\n\n\nSince `clone()` can be used to instantiate objects without using a constructor, allowing the `clone()`\nmethod to be overridden may result in corrupted objects, and even in security exploits. This may be prevented by making the\n`clone()` method or the enclosing class itself `final`.\n\n**Example:**\n\n\n class Main implements Cloneable {\n @Override\n protected Object clone() throws CloneNotSupportedException {\n return super.clone();\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonFinalClone", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Security", + "index": 30, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ChainedEquality", + "shortDescription": { + "text": "Chained equality comparisons" + }, + "fullDescription": { + "text": "Reports chained equality comparisons. Such comparisons may be confusing: 'a == b == c' means '(a == b) == c', but possibly 'a == b && a == c' is intended. Example: 'boolean chainedEquality(boolean a, boolean b, boolean c) {\n return a == b == c;\n }' You can use parentheses to make the comparison less confusing: 'boolean chainedEquality(boolean a, boolean b, boolean c) {\n return (a == b) == c;\n }'", + "markdown": "Reports chained equality comparisons.\n\nSuch comparisons may be confusing: `a == b == c` means `(a == b) == c`,\nbut possibly `a == b && a == c` is intended.\n\n**Example:**\n\n\n boolean chainedEquality(boolean a, boolean b, boolean c) {\n return a == b == c;\n }\n\nYou can use parentheses to make the comparison less confusing:\n\n\n boolean chainedEquality(boolean a, boolean b, boolean c) {\n return (a == b) == c;\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ChainedEqualityComparisons", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AbstractMethodOverridesConcreteMethod", + "shortDescription": { + "text": "Abstract method overrides concrete method" + }, + "fullDescription": { + "text": "Reports 'abstract' methods that override concrete super methods. Methods overridden from 'java.lang.Object' are not reported by this inspection.", + "markdown": "Reports `abstract` methods that override concrete super methods.\n\nMethods overridden from `java.lang.Object` are not reported by this inspection." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AbstractMethodOverridesConcreteMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Inheritance issues", + "index": 25, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LossyConversionCompoundAssignment", + "shortDescription": { + "text": "Possibly lossy implicit cast in compound assignment" + }, + "fullDescription": { + "text": "Reports compound assignments if the type of the right-hand operand is not assignment compatible with the type of the variable. During such compound assignments, an implicit cast occurs, potentially resulting in lossy conversions. Example: 'long c = 1;\n c += 1.2;' After the quick-fix is applied: 'long c = 1;\n c += (long) 1.2;' New in 2023.2", + "markdown": "Reports compound assignments if the type of the right-hand operand is not assignment compatible with the type of the variable.\n\n\nDuring such compound assignments, an implicit cast occurs, potentially resulting in lossy conversions.\n\nExample:\n\n\n long c = 1;\n c += 1.2;\n\nAfter the quick-fix is applied:\n\n\n long c = 1;\n c += (long) 1.2;\n\nNew in 2023.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "lossy-conversions", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ThrowFromFinallyBlock", + "shortDescription": { + "text": "'throw' inside 'finally' block" + }, + "fullDescription": { + "text": "Reports 'throw' statements inside 'finally' blocks. While occasionally intended, such 'throw' statements may conceal exceptions thrown from 'try'-'catch' and thus tremendously complicate the debugging process.", + "markdown": "Reports `throw` statements inside `finally` blocks.\n\nWhile occasionally intended, such `throw` statements may conceal exceptions thrown from `try`-`catch` and thus\ntremendously complicate the debugging process." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ThrowFromFinallyBlock", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StaticNonFinalField", + "shortDescription": { + "text": "'static', non-'final' field" + }, + "fullDescription": { + "text": "Reports non-'final' 'static' fields. A quick-fix is available to add the 'final' modifier to a non-'final' 'static' field. This inspection doesn't check fields' mutability. For example, adding the 'final' modifier to a field that has a value being set somewhere will cause a compilation error. Use the Only report 'public' fields option so that the inspection reported only 'public' fields.", + "markdown": "Reports non-`final` `static` fields.\n\nA quick-fix is available to add the `final` modifier to a non-`final` `static` field.\n\nThis inspection doesn't check fields' mutability. For example, adding the `final` modifier to a field that has a value\nbeing set somewhere will cause a compilation error.\n\n\nUse the **Only report 'public' fields** option so that the inspection reported only `public` fields." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StaticNonFinalField", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CodeBlock2Expr", + "shortDescription": { + "text": "Statement lambda can be replaced with expression lambda" + }, + "fullDescription": { + "text": "Reports lambda expressions with code block bodies when expression-style bodies can be used instead. The result of the conversion is shorter and more clear. Example: 'Comparable c = o -> {return 0;};' After the quick-fix is applied: 'Comparable c = o -> 0;'", + "markdown": "Reports lambda expressions with code block bodies when expression-style bodies can be used instead. The result of the conversion is shorter and more clear.\n\nExample:\n\n\n Comparable c = o -> {return 0;};\n\nAfter the quick-fix is applied:\n\n\n Comparable c = o -> 0;\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CodeBlock2Expr", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 8", + "index": 62, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SimplifyForEach", + "shortDescription": { + "text": "Simplifiable forEach() call" + }, + "fullDescription": { + "text": "Reports 'forEach()' calls that can be replaced with a more concise method or from which intermediate steps can be extracted. Example: 'List findNStrings(List list, int n) {\n List other = new ArrayList<>();\n list.forEach(s -> {\n if(s.length() > n) other.add(s);\n });\n return other;\n }' After the quick-fix is applied: 'List findNStrings(List list, int n) {\n List other = list.stream()\n .filter(s -> s.length() > n)\n .collect(Collectors.toList());\n return other;\n }' This inspection only reports if the language level of the project or module is 8 or higher. New in 2017.3", + "markdown": "Reports `forEach()` calls that can be replaced with a more concise method or from which intermediate steps can be extracted.\n\n**Example:**\n\n\n List findNStrings(List list, int n) {\n List other = new ArrayList<>();\n list.forEach(s -> {\n if(s.length() > n) other.add(s);\n });\n return other;\n }\n\nAfter the quick-fix is applied:\n\n\n List findNStrings(List list, int n) {\n List other = list.stream()\n .filter(s -> s.length() > n)\n .collect(Collectors.toList());\n return other;\n }\n\nThis inspection only reports if the language level of the project or module is 8 or higher.\n\nNew in 2017.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "SimplifyForEach", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 8", + "index": 62, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EmptyMethod", + "shortDescription": { + "text": "Empty method" + }, + "fullDescription": { + "text": "Reports empty methods that can be removed. Methods are considered empty if they are empty themselves and if they are overridden or implemented by empty methods only. Note that methods containing only comments and the 'super()' call with own parameters are also considered empty. The inspection ignores methods with special annotations, for example, the 'javax.ejb.Init' and 'javax.ejb.Remove' EJB annotations . The quick-fix safely removes unnecessary methods. Configure the inspection: Use the Comments and javadoc count as content option to select whether methods with comments should be treated as non-empty. Use the Additional special annotations option to configure additional annotations that should be ignored by this inspection.", + "markdown": "Reports empty methods that can be removed.\n\nMethods are considered empty if they are empty themselves and if they are overridden or\nimplemented by empty methods only. Note that methods containing only comments and the `super()` call with own parameters are\nalso considered empty.\n\nThe inspection ignores methods with special annotations, for example, the `javax.ejb.Init` and `javax.ejb.Remove` EJB annotations .\n\nThe quick-fix safely removes unnecessary methods.\n\nConfigure the inspection:\n\n* Use the **Comments and javadoc count as content** option to select whether methods with comments should be treated as non-empty.\n* Use the **Additional special annotations** option to configure additional annotations that should be ignored by this inspection." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EmptyMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantFieldInitialization", + "shortDescription": { + "text": "Redundant field initialization" + }, + "fullDescription": { + "text": "Reports fields explicitly initialized to their default values. Example: 'class Foo {\n int foo = 0;\n List bar = null;\n }' After the quick-fix is applied: 'class Foo {\n int foo;\n List bar;\n }' Use the inspection settings to only report explicit 'null' initialization, for example: 'class Foo {\n int foo = 0; // no warning\n List bar = null; // redundant field initialization warning\n }'", + "markdown": "Reports fields explicitly initialized to their default values.\n\n**Example:**\n\n\n class Foo {\n int foo = 0;\n List bar = null;\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo {\n int foo;\n List bar;\n }\n\n\nUse the inspection settings to only report explicit `null` initialization, for example:\n\n\n class Foo {\n int foo = 0; // no warning\n List bar = null; // redundant field initialization warning\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantFieldInitialization", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Since15", + "shortDescription": { + "text": "Usages of API which isn't available at the configured language level" + }, + "fullDescription": { + "text": "Reports usages of the API that is unavailable at the configured language level. This inspection does 3 things: Highlight usage of generified classes when the language level is below Java 7. Highlight when default methods are not overridden and the language level is below Java 8. Highlight usage of API when the language level is lower than marked using the '@since' tag in the documentation. Use the Forbid API usages option to forbid usages of the API in respect to the project or custom language level.", + "markdown": "Reports usages of the API that is unavailable at the configured language level. This inspection does 3 things:\n\n* Highlight usage of generified classes when the language level is below Java 7.\n* Highlight when default methods are not overridden and the language level is below Java 8.\n* Highlight usage of API when the language level is lower than marked using the `@since` tag in the documentation.\n\n\nUse the **Forbid API usages** option to forbid usages of the API in respect to the project or custom language level." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "Since15", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages", + "index": 2, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SimplifiableEqualsExpression", + "shortDescription": { + "text": "Unnecessary 'null' check before 'equals()' call" + }, + "fullDescription": { + "text": "Reports comparisons to 'null' that are followed by a call to 'equals()' with a constant argument. Example: 'if (s != null && s.equals(\"literal\")) {}' After the quick-fix is applied: 'if (\"literal\".equals(s)) {}' Use the inspection settings to report 'equals()' calls with a non-constant argument when the argument to 'equals()' is proven not to be 'null'.", + "markdown": "Reports comparisons to `null` that are followed by a call to `equals()` with a constant argument.\n\n**Example:**\n\n\n if (s != null && s.equals(\"literal\")) {}\n\nAfter the quick-fix is applied:\n\n\n if (\"literal\".equals(s)) {}\n\n\nUse the inspection settings to report `equals()` calls with a non-constant argument\nwhen the argument to `equals()` is proven not to be `null`." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SimplifiableEqualsExpression", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonCommentSourceStatements", + "shortDescription": { + "text": "Overly long method" + }, + "fullDescription": { + "text": "Reports methods whose number of statements exceeds the specified maximum. Methods with too many statements may be confusing and are a good sign that refactoring is necessary. The following statements are not counted: empty statements (semicolons) block statements 'for' loop initialization statements, that is, 'int i = ...' within a 'for(int i = ...;...)' statement 'for' loop update statements, that is, 'i += 2' within a 'for(int i = ...;...; i += 2)' statement Use the Maximum statements per method field to specify the maximum allowed number of statements in a method.", + "markdown": "Reports methods whose number of statements exceeds the specified maximum.\n\nMethods with too many statements may be confusing and are a good sign that refactoring is necessary.\n\nThe following statements are not counted:\n\n* empty statements (semicolons)\n* block statements\n* `for` loop initialization statements, that is, `int i = ...` within a `for(int i = ...;...)` statement\n* `for` loop update statements, that is, `i += 2` within a `for(int i = ...;...; i += 2)` statement\n\nUse the **Maximum statements per method** field to specify the maximum allowed number of statements in a method." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OverlyLongMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Method metrics", + "index": 95, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConfusingMainMethod", + "shortDescription": { + "text": "Confusing 'main()' method" + }, + "fullDescription": { + "text": "Reports methods that are named \"main\", but do not have the 'public static void main(String[])' signature in Java up to 21. Starting from Java 21 Preview, inspection doesn't highlight in case of package-private, protected or instance main methods, also without parameters. Additionally main methods located in anonymous or local classes are reported. Anonymous and local classes do not have a fully qualified name and thus can't be run. Such methods may be confusing, as methods named \"main\" are expected to be application entry points. Example: 'class Main {\n void main(String[] args) {} // warning here because there are no \"public static\" modifiers\n }' A quick-fix that renames such methods is available only in the editor.", + "markdown": "Reports methods that are named \"main\", but do not have the `public static void main(String[])` signature in Java up to 21. Starting from Java 21 Preview, inspection doesn't highlight in case of package-private, protected or instance main methods, also without parameters. Additionally main methods located in anonymous or local classes are reported. Anonymous and local classes do not have a fully qualified name and thus can't be run.\n\nSuch methods may be confusing, as methods named \"main\"\nare expected to be application entry points.\n\n**Example:**\n\n\n class Main {\n void main(String[] args) {} // warning here because there are no \"public static\" modifiers\n }\n\nA quick-fix that renames such methods is available only in the editor." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ConfusingMainMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReadObjectInitialization", + "shortDescription": { + "text": "Instance field may not be initialized by 'readObject()'" + }, + "fullDescription": { + "text": "Reports fields that are not guaranteed to be initialized after the object is deserialized by the 'readObject()' method. The inspection doesn't report transient fields. Note: This inspection uses a very conservative control flow algorithm, and may incorrectly report fields as uninitialized. Example: 'class DataObject implements Serializable {\n String s; // s is not initialized in readObject\n int i;\n\n private void readObject(ObjectInputStream stream) throws IOException {\n i = stream.readInt();\n }\n}'", + "markdown": "Reports fields that are not guaranteed to be initialized after the object is deserialized by the `readObject()` method.\n\nThe inspection doesn't report transient fields.\n\n\nNote: This inspection uses a very conservative control flow algorithm, and may incorrectly report fields\nas uninitialized.\n\n**Example:**\n\n\n class DataObject implements Serializable {\n String s; // s is not initialized in readObject\n int i;\n\n private void readObject(ObjectInputStream stream) throws IOException {\n i = stream.readInt();\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "InstanceVariableMayNotBeInitializedByReadObject", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonAtomicOperationOnVolatileField", + "shortDescription": { + "text": "Non-atomic operation on 'volatile' field" + }, + "fullDescription": { + "text": "Reports non-atomic operations on volatile fields. An example of a non-atomic operation is updating the field using the increment operator. As the operation involves read and write, and other modifications may happen in between, data may become corrupted. The operation can be made atomic by surrounding it with a 'synchronized' block or using one of the classes from the 'java.util.concurrent.atomic' package. Example: 'private volatile int v = 1;\n\n void foo() {\n v = 2 * v;\n }'", + "markdown": "Reports non-atomic operations on volatile fields.\n\n\nAn example of a non-atomic operation is updating the field using the increment operator.\nAs the operation involves read and write, and other modifications may happen in between, data may become corrupted.\nThe operation can be made atomic by surrounding it with a `synchronized` block or\nusing one of the classes from the `java.util.concurrent.atomic` package.\n\n**Example:**\n\n\n private volatile int v = 1;\n\n void foo() {\n v = 2 * v;\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "NonAtomicOperationOnVolatileField", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MultipleVariablesInDeclaration", + "shortDescription": { + "text": "Multiple variables in one declaration" + }, + "fullDescription": { + "text": "Reports multiple variables that are declared in a single declaration and suggest creating a separate declaration for each variable. Some coding standards prohibit such declarations. Example: 'int x = 1, y = 2;' After the quick-fix is applied: 'int x = 1;\n int y = 2;' Configure the inspection: Use the Ignore 'for' loop declarations option to ignore multiple variables declared in the initialization of a 'for' loop statement, for example: 'for (int i = 0, max = list.size(); i > max; i++) {}' Use the Only warn on different array dimensions in a single declaration option to only warn when variables with different array dimensions are declared in a single declaration, for example: 'String s = \"\", array[];' New in 2019.2", + "markdown": "Reports multiple variables that are declared in a single declaration and suggest creating a separate declaration for each variable.\n\nSome coding standards prohibit such declarations.\n\nExample:\n\n\n int x = 1, y = 2;\n\nAfter the quick-fix is applied:\n\n\n int x = 1;\n int y = 2;\n\nConfigure the inspection:\n\n* Use the **Ignore 'for' loop declarations** option to ignore multiple variables declared in the initialization of a 'for' loop statement, for example:\n\n\n for (int i = 0, max = list.size(); i > max; i++) {}\n\n* Use the **Only warn on different array dimensions in a single declaration** option to only warn when variables with different array dimensions are declared in a single declaration, for example:\n\n\n String s = \"\", array[];\n\nNew in 2019.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "MultipleVariablesInDeclaration", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "QuestionableName", + "shortDescription": { + "text": "Questionable name" + }, + "fullDescription": { + "text": "Reports variables, methods, or classes with questionable, not really descriptive names. Such names do not help to understand the code, and most probably were created as a temporary thing but were forgotten afterwards. Example: 'int aa = 42;' Rename quick-fix is suggested only in the editor. Use the option to list names that should be reported.", + "markdown": "Reports variables, methods, or classes with questionable, not really descriptive names. Such names do not help to understand the code, and most probably were created as a temporary thing but were forgotten afterwards.\n\n**Example:**\n\n\n int aa = 42;\n\nRename quick-fix is suggested only in the editor.\n\n\nUse the option to list names that should be reported." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "QuestionableName", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions", + "index": 51, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IOResource", + "shortDescription": { + "text": "I/O resource opened but not safely closed" + }, + "fullDescription": { + "text": "Reports I/O resources that are not safely closed. I/O resources checked by this inspection include 'java.io.InputStream', 'java.io.OutputStream', 'java.io.Reader', 'java.io.Writer', 'java.util.zip.ZipFile', 'java.io.Closeable' and 'java.io.RandomAccessFile'. I/O resources wrapped by other I/O resources are not reported, as the wrapped resource will be closed by the wrapping resource. By default, the inspection assumes that the resources can be closed by any method with 'close' or 'cleanup' in its name. Example: 'void save() throws IOException {\n FileWriter writer = new FileWriter(\"filename.txt\"); //warning\n writer.write(\"sample\");\n }' Use the following options to configure the inspection: List I/O resource classes that do not need to be closed and should be ignored by this inspection. Whether an I/O resource is allowed to be opened inside a 'try'block. This style is less desirable because it is more verbose than opening a resource in front of a 'try' block. Whether the resource can be closed by any method call with the resource passed as argument.", + "markdown": "Reports I/O resources that are not safely closed. I/O resources checked by this inspection include `java.io.InputStream`, `java.io.OutputStream`, `java.io.Reader`, `java.io.Writer`, `java.util.zip.ZipFile`, `java.io.Closeable` and `java.io.RandomAccessFile`.\n\n\nI/O resources wrapped by other I/O resources are not reported, as the wrapped resource will be closed by the wrapping resource.\n\n\nBy default, the inspection assumes that the resources can be closed by any method with\n'close' or 'cleanup' in its name.\n\n**Example:**\n\n\n void save() throws IOException {\n FileWriter writer = new FileWriter(\"filename.txt\"); //warning\n writer.write(\"sample\");\n }\n\n\nUse the following options to configure the inspection:\n\n* List I/O resource classes that do not need to be closed and should be ignored by this inspection.\n* Whether an I/O resource is allowed to be opened inside a `try`block. This style is less desirable because it is more verbose than opening a resource in front of a `try` block.\n* Whether the resource can be closed by any method call with the resource passed as argument." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "IOResourceOpenedButNotSafelyClosed", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Resource management", + "index": 47, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantLengthCheck", + "shortDescription": { + "text": "Redundant array length check" + }, + "fullDescription": { + "text": "Reports unnecessary array length checks followed by array iteration. When array length is zero, the iteration will be skipped anyway, so there's no need to check length explicitly. Example: 'void f(String[] array) {\n if (array.length != 0) { // unnecessary check\n for (String str : array) {\n System.out.println(str);\n }\n }\n }' A quick-fix is suggested to unwrap or remove the length check: 'void f(String[] array) {\n for (String str : array) {\n System.out.println(str);\n }\n }' New in 2022.3", + "markdown": "Reports unnecessary array length checks followed by array iteration. When array length is zero, the iteration will be skipped anyway, so there's no need to check length explicitly.\n\nExample:\n\n\n void f(String[] array) {\n if (array.length != 0) { // unnecessary check\n for (String str : array) {\n System.out.println(str);\n }\n }\n }\n\nA quick-fix is suggested to unwrap or remove the length check:\n\n\n void f(String[] array) {\n for (String str : array) {\n System.out.println(str);\n }\n }\n\nNew in 2022.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantLengthCheck", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UNCHECKED_WARNING", + "shortDescription": { + "text": "Unchecked warning" + }, + "fullDescription": { + "text": "Reports code on which an unchecked warning will be issued by the javac compiler. Every unchecked warning may potentially trigger 'ClassCastException' at runtime. Example: 'List items = Arrays.asList(\"string\", \"string\");\n List numbers = Collections.unmodifiableList(items); // unchecked assignment'", + "markdown": "Reports code on which an unchecked warning will be issued by the javac compiler. Every unchecked warning may potentially trigger `ClassCastException` at runtime.\n\nExample:\n\n\n List items = Arrays.asList(\"string\", \"string\");\n List numbers = Collections.unmodifiableList(items); // unchecked assignment\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "unchecked", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Compiler issues", + "index": 90, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DataFlowIssue", + "shortDescription": { + "text": "Nullability and data flow problems" + }, + "fullDescription": { + "text": "Reports code constructs that always violate nullability contracts, may throw exceptions, or are just redundant, based on data flow analysis. Examples: 'if (array.length < index) {\n System.out.println(array[index]);\n} // Array index is always out of bounds\n\nif (str == null) System.out.println(\"str is null\");\nSystem.out.println(str.trim());\n// the last statement may throw an NPE\n\n@NotNull\nInteger square(@Nullable Integer input) {\n // the method contract is violated\n return input == null ? null : input * input;\n}' The inspection behavior may be controlled by a number of annotations, such as nullability annotations, '@Contract' annotation, '@Range' annotation and so on. Configure the inspection: Use the Suggest @Nullable annotation for methods/fields/parameters where nullable values are used option to warn when a nullable value is passed as an argument to a method with a non-annotated parameter, stored into non-annotated field, or returned from a non-annotated method. In this case, the inspection will suggest propagating the '@Nullable' annotation. You can also configure nullability annotations using the Configure Annotations button. Use the Treat non-annotated members and parameters as @Nullable option to assume that non-annotated members can be null, so they must not be used in non-null context. Use the Report not-null required parameter with null-literal argument usages option to report method parameters that cannot be null (e.g. immediately dereferenced in the method body), but there are call sites where a 'null' literal is passed. Use the Report nullable methods that always return a non-null value option to report methods that are annotated as '@Nullable', but always return non-null value. In this case, it's suggested that you change the annotation to '@NotNull'. Use the Ignore assert statements option to control how the inspection treats 'assert' statements. By default, the option is disabled, which means that the assertions are assumed to be executed (-ea mode). If the option is enabled, the assertions will be completely ignored (-da mode). Use the Report problems that happen only on some code paths option to control whether to report problems that may happen only on some code path. If this option is disabled, warnings like exception is possible will not be reported. The inspection will report only warnings like exception will definitely occur. This mode may greatly reduce the number of false-positives, especially if the code is not consistently annotated with nullability and contract annotations. That is why it can be useful for finding the most important problems in legacy code bases. Before IntelliJ IDEA 2022.3, this inspection was part of the \"Constant Conditions & Exceptions\" inspection. Now, it is split into two inspections: \"Constant Values\" and \"Nullability and data flow problems\".", + "markdown": "Reports code constructs that always violate nullability contracts, may throw exceptions, or are just redundant, based on data flow analysis.\n\nExamples:\n\n if (array.length < index) {\n System.out.println(array[index]);\n } // Array index is always out of bounds\n\n if (str == null) System.out.println(\"str is null\");\n System.out.println(str.trim());\n // the last statement may throw an NPE\n\n @NotNull\n Integer square(@Nullable Integer input) {\n // the method contract is violated\n return input == null ? null : input * input;\n }\n\n\nThe inspection behavior may be controlled by a number of annotations, such as\n[nullability](https://www.jetbrains.com/help/idea/nullable-and-notnull-annotations.html) annotations,\n[@Contract](https://www.jetbrains.com/help/idea/contract-annotations.html) annotation,\n`@Range` annotation and so on.\n\nConfigure the inspection:\n\n* Use the **Suggest @Nullable annotation for methods/fields/parameters where nullable values are used** option to warn when a nullable value is passed as an argument to a method with a non-annotated parameter, stored into non-annotated field, or returned from a non-annotated method. In this case, the inspection will suggest propagating the `@Nullable` annotation. You can also configure nullability annotations using the **Configure Annotations** button.\n* Use the **Treat non-annotated members and parameters as @Nullable** option to assume that non-annotated members can be null, so they must not be used in non-null context.\n* Use the **Report not-null required parameter with null-literal argument usages** option to report method parameters that cannot be null (e.g. immediately dereferenced in the method body), but there are call sites where a `null` literal is passed.\n* Use the **Report nullable methods that always return a non-null value** option to report methods that are annotated as `@Nullable`, but always return non-null value. In this case, it's suggested that you change the annotation to `@NotNull`.\n* Use the **Ignore assert statements** option to control how the inspection treats `assert` statements. By default, the option is disabled, which means that the assertions are assumed to be executed (-ea mode). If the option is enabled, the assertions will be completely ignored (-da mode).\n* Use the **Report problems that happen only on some code paths** option to control whether to report problems that may happen only on some code path. If this option is disabled, warnings like *exception is possible* will not be reported. The inspection will report only warnings like *exception will definitely occur*. This mode may greatly reduce the number of false-positives, especially if the code is not consistently annotated with nullability and contract annotations. That is why it can be useful for finding the most important problems in legacy code bases.\n\n\nBefore IntelliJ IDEA 2022.3, this inspection was part of the \"Constant Conditions \\& Exceptions\" inspection.\nNow, it is split into two inspections:\n\"Constant Values\" and \"Nullability and data flow problems\"." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "DataFlowIssue", + "cweIds": [ + 129, + 252, + 253, + 394, + 395, + 476, + 570, + 571, + 690 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ParameterCanBeLocal", + "shortDescription": { + "text": "Value passed as parameter never read" + }, + "fullDescription": { + "text": "Reports redundant method parameters that can be replaced with local variables. If all local usages of a parameter are preceded by assignments to that parameter, the parameter can be removed and its usages replaced with local variables. It makes no sense to have such a parameter, as values that are passed to it are overwritten. Usually, the problem appears as a result of refactoring. Example: 'void test(int p) {\n p = 1;\n System.out.print(p);\n }' After the quick-fix is applied: 'void test() {\n int p = 1;\n System.out.print(p);\n }'", + "markdown": "Reports redundant method parameters that can be replaced with local variables.\n\nIf all local usages of a parameter are preceded by assignments to that parameter, the\nparameter can be removed and its usages replaced with local variables.\nIt makes no sense to have such a parameter, as values that are passed to it are overwritten.\nUsually, the problem appears as a result of refactoring.\n\nExample:\n\n\n void test(int p) {\n p = 1;\n System.out.print(p);\n }\n\nAfter the quick-fix is applied:\n\n\n void test() {\n int p = 1;\n System.out.print(p);\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ParameterCanBeLocal", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CallToSimpleGetterInClass", + "shortDescription": { + "text": "Call to simple getter from within class" + }, + "fullDescription": { + "text": "Reports calls to a simple property getter from within the property's class. A simple property getter is defined as one which simply returns the value of a field, and does no other calculations. Such simple getter calls can be safely inlined using the quick-fix. Some coding standards also suggest against the use of simple getters for code clarity reasons. Example: 'public class Salient {\n private String name;\n\n public String getName() {\n return name;\n }\n\n @Override\n public String toString() {\n return getName();\n }\n }' After the quick-fix is applied: 'public class Salient {\n private String name;\n\n public String getName() {\n return name;\n }\n\n @Override\n public String toString() {\n return name;\n }\n }' Use the following options to configure the inspection: Whether to only report getter calls on 'this', not on objects of the same type passed in as a parameter. Whether to ignore non-'private' getters.", + "markdown": "Reports calls to a simple property getter from within the property's class.\n\n\nA simple property getter is defined as one which simply returns the value of a field,\nand does no other calculations. Such simple getter calls can be safely inlined using the quick-fix.\nSome coding standards also suggest against the use of simple getters for code clarity reasons.\n\n**Example:**\n\n\n public class Salient {\n private String name;\n\n public String getName() {\n return name;\n }\n\n @Override\n public String toString() {\n return getName();\n }\n }\n\nAfter the quick-fix is applied:\n\n\n public class Salient {\n private String name;\n\n public String getName() {\n return name;\n }\n\n @Override\n public String toString() {\n return name;\n }\n }\n\nUse the following options to configure the inspection:\n\n* Whether to only report getter calls on `this`, not on objects of the same type passed in as a parameter.\n* Whether to ignore non-`private` getters." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToSimpleGetterFromWithinClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SwitchStatementDensity", + "shortDescription": { + "text": "'switch' statement with too low of a branch density" + }, + "fullDescription": { + "text": "Reports 'switch' statements or expressions with a too low ratio of switch labels to executable statements. Such 'switch' statements may be confusing and should probably be refactored. Example: 'switch (i) { // one case and 5 executable statements -> 20% density\n case 1:\n System.out.println(\"1\");\n System.out.println(\"2\");\n System.out.println(\"3\");\n System.out.println(\"4\");\n System.out.println(\"5\");\n break;\n }' Use the Minimum density of branches field to specify the allowed ratio of the switch labels to executable statements.", + "markdown": "Reports `switch` statements or expressions with a too low ratio of switch labels to executable statements.\n\nSuch `switch` statements\nmay be confusing and should probably be refactored.\n\nExample:\n\n\n switch (i) { // one case and 5 executable statements -> 20% density\n case 1:\n System.out.println(\"1\");\n System.out.println(\"2\");\n System.out.println(\"3\");\n System.out.println(\"4\");\n System.out.println(\"5\");\n break;\n }\n\n\nUse the **Minimum density of branches** field to specify the allowed ratio of the switch labels to executable statements." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SwitchStatementDensity", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonSerializableFieldInSerializableClass", + "shortDescription": { + "text": "Non-serializable field in a 'Serializable' class" + }, + "fullDescription": { + "text": "Reports non-serializable fields in classes that implement 'java.io.Serializable'. Such fields will result in runtime exceptions if the object is serialized. Fields declared 'transient' or 'static' are not reported, nor are fields of classes that have a 'writeObject' method defined. This inspection assumes fields of the types 'java.util.Collection' and 'java.util.Map' to be 'Serializable', unless the types they are declared in are non-'Serializable'. Example: 'class NonSerializableClass {}\n\n public class SerializableClass implements Serializable {\n NonSerializableClass clazz; // warning: Non-serializable field 'clazz' in a Serializable class\n static NonSerializableClass staticClazz; // no warnings\n }'\n Use the following options to configure the inspection: List classes whose inheritors should not be reported by this inspection. This is meant for classes that inherit 'Serializable' from a superclass but are not intended for serialization. List annotations that will make the inspection ignore the annotated fields. Whether to ignore fields initialized with an anonymous class.", + "markdown": "Reports non-serializable fields in classes that implement `java.io.Serializable`. Such fields will result in runtime exceptions if the object is serialized.\n\n\nFields declared\n`transient` or `static`\nare not reported, nor are fields of classes that have a `writeObject` method defined.\n\n\nThis inspection assumes fields of the types\n`java.util.Collection` and\n`java.util.Map` to be\n`Serializable`, unless the types\nthey are declared in are non-`Serializable`.\n\n**Example:**\n\n\n class NonSerializableClass {}\n\n public class SerializableClass implements Serializable {\n NonSerializableClass clazz; // warning: Non-serializable field 'clazz' in a Serializable class\n static NonSerializableClass staticClazz; // no warnings\n }\n \n\nUse the following options to configure the inspection:\n\n* List classes whose inheritors should not be reported by this inspection. This is meant for classes that inherit `Serializable` from a superclass but are not intended for serialization.\n* List annotations that will make the inspection ignore the annotated fields.\n* Whether to ignore fields initialized with an anonymous class." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonSerializableFieldInSerializableClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MismatchedJavadocCode", + "shortDescription": { + "text": "Mismatch between Javadoc and code" + }, + "fullDescription": { + "text": "Reports parts of method specification written in English that contradict with the method declaration. This includes: Method specified to return 'true' or 'false' but its return type is not boolean. Method specified to return 'null' but it's annotated as '@NotNull' or its return type is primitive. Method specified to return list but its return type is set or array. And so on. Example: '/**\n * @return true if user is found, false otherwise\n */\n User findUser(String name);' Note that false-positives are possible, as this inspection tries to interpret a human language. However, if the inspection reports incorrectly, it's still possible that the description is confusing and should be rewritten. New in 2022.3", + "markdown": "Reports parts of method specification written in English that contradict with the method declaration. This includes:\n\n* Method specified to return `true` or `false` but its return type is not boolean.\n* Method specified to return `null` but it's annotated as `@NotNull` or its return type is primitive.\n* Method specified to return list but its return type is set or array.\n* And so on.\n\n**Example:**\n\n\n /**\n * @return true if user is found, false otherwise\n */\n User findUser(String name);\n\n\nNote that false-positives are possible, as this inspection tries to interpret a human language. However, if the inspection reports\nincorrectly, it's still possible that the description is confusing and should be rewritten.\n\n\nNew in 2022.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MismatchedJavadocCode", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Javadoc", + "index": 45, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EnhancedSwitchMigration", + "shortDescription": { + "text": "Statement can be replaced with enhanced 'switch'" + }, + "fullDescription": { + "text": "Reports 'switch' statements that can be automatically replaced with enhanced 'switch' statements or expressions. Example: 'double getPrice(String fruit) {\n // Switch statement can be replaced with enhanced 'switch'\n switch (fruit) {\n case \"Apple\":\n return 1.0;\n case \"Orange\":\n return 1.5;\n case \"Mango\":\n return 2.0;\n default:\n throw new IllegalArgumentException();\n }\n }' After the quick-fix is applied: 'double getPrice(String fruit) {\n return switch (fruit) {\n case \"Apple\" -> 1.0;\n case \"Orange\" -> 1.5;\n case \"Mango\" -> 2.0;\n default -> throw new IllegalArgumentException();\n };\n }' Use the Show warning only if conversion to expression is possible option not to warn about conversion to 'switch' statement. Use the Maximum number of statements in one branch to convert to switch expression option warn about conversion to expression only if each branch has less than the given number of statements. This inspection only reports if the language level of the project or module is 14 or higher New in 2019.1", + "markdown": "Reports `switch` statements that can be automatically replaced with enhanced `switch` statements or expressions.\n\n**Example:**\n\n\n double getPrice(String fruit) {\n // Switch statement can be replaced with enhanced 'switch'\n switch (fruit) {\n case \"Apple\":\n return 1.0;\n case \"Orange\":\n return 1.5;\n case \"Mango\":\n return 2.0;\n default:\n throw new IllegalArgumentException();\n }\n }\n\nAfter the quick-fix is applied:\n\n\n double getPrice(String fruit) {\n return switch (fruit) {\n case \"Apple\" -> 1.0;\n case \"Orange\" -> 1.5;\n case \"Mango\" -> 2.0;\n default -> throw new IllegalArgumentException();\n };\n }\n \n\n* Use the **Show warning only if conversion to expression is possible** option not to warn about conversion to `switch` statement.\n* Use the **Maximum number of statements in one branch to convert to switch expression** option warn about conversion to expression only if each branch has less than the given number of statements.\n\nThis inspection only reports if the language level of the project or module is 14 or higher\n\nNew in 2019.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EnhancedSwitchMigration", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 14", + "index": 100, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SimplifiableAnnotation", + "shortDescription": { + "text": "Simplifiable annotation" + }, + "fullDescription": { + "text": "Reports annotations that can be simplified to their single-element or marker shorthand form. Problems reported: Redundant 'value=' in annotation name-value pairs Redundant braces around array values that contain only a single value Redundant whitespace between the @-sign and the name of annotations Redundant whitespace between annotation names and parameter lists Redundant parentheses in annotations without any parameters Example: '@interface Foo { String[] value(); }\n\n @ Foo({\"foo\"})\n public String name;' After the quick-fix is applied: '@interface Foo { String[] value(); }\n\n @Foo(\"foo\")\n public String name;'", + "markdown": "Reports annotations that can be simplified to their single-element or marker shorthand form.\n\n\nProblems reported:\n\n* Redundant `value=` in annotation name-value pairs\n* Redundant braces around array values that contain only a single value\n* Redundant whitespace between the @-sign and the name of annotations\n* Redundant whitespace between annotation names and parameter lists\n* Redundant parentheses in annotations without any parameters\n\n**Example:**\n\n\n @interface Foo { String[] value(); }\n\n @ Foo({\"foo\"})\n public String name;\n\nAfter the quick-fix is applied:\n\n\n @interface Foo { String[] value(); }\n\n @Foo(\"foo\")\n public String name;\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SimplifiableAnnotation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DoubleLiteralMayBeFloatLiteral", + "shortDescription": { + "text": "Cast to 'float' can be 'float' literal" + }, + "fullDescription": { + "text": "Reports 'double' literal expressions that are immediately cast to 'float'. Such literal expressions can be replaced with equivalent 'float' literals. Example: 'float f = (float)1.1;' After the quick-fix is applied: 'float f = 1.1f;'", + "markdown": "Reports `double` literal expressions that are immediately cast to `float`.\n\nSuch literal expressions can be replaced with equivalent `float` literals.\n\n**Example:**\n\n float f = (float)1.1;\n\nAfter the quick-fix is applied:\n\n float f = 1.1f;\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "DoubleLiteralMayBeFloatLiteral", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues/Cast", + "index": 102, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Guava", + "shortDescription": { + "text": "Guava's functional primitives can be replaced with Java" + }, + "fullDescription": { + "text": "Reports usages of Guava's functional primitives that can be migrated to standard Java API calls. For example, the inspection reports usages of classes and interfaces like 'FluentIterable', 'Optional', 'Function', 'Predicate', or 'Supplier'. Example: 'ImmutableList results = FluentIterable.from(List.of(1, 2, 3)).transform(Object::toString).toList();' After the quick-fix is applied: 'List results = List.of(1, 2, 3).stream().map(Object::toString).collect(Collectors.toList());' The quick-fix may change the semantics. Some lazy-evaluated Guava's iterables can be transformed to eager-evaluated. This inspection only reports if the language level of the project or module is 8 or higher.", + "markdown": "Reports usages of Guava's functional primitives that can be migrated to standard Java API calls.\n\nFor example, the inspection reports usages of classes and interfaces like `FluentIterable`, `Optional`, `Function`,\n`Predicate`, or `Supplier`.\n\nExample:\n\n\n ImmutableList results = FluentIterable.from(List.of(1, 2, 3)).transform(Object::toString).toList();\n\nAfter the quick-fix is applied:\n\n\n List results = List.of(1, 2, 3).stream().map(Object::toString).collect(Collectors.toList());\n\n\nThe quick-fix may change the semantics. Some lazy-evaluated Guava's iterables can be transformed to eager-evaluated.\n\nThis inspection only reports if the language level of the project or module is 8 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "Guava", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 8", + "index": 62, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NoopMethodInAbstractClass", + "shortDescription": { + "text": "No-op method in 'abstract' class" + }, + "fullDescription": { + "text": "Reports no-op (for \"no operation\") methods in 'abstract' classes. It is usually a better design to make such methods 'abstract' themselves so that classes inheriting these methods provide their implementations. Example: 'abstract class Test {\n protected void doTest() {\n }\n }'", + "markdown": "Reports no-op (for \"no operation\") methods in `abstract` classes.\n\nIt is usually a better\ndesign to make such methods `abstract` themselves so that classes inheriting these\nmethods provide their implementations.\n\n**Example:**\n\n\n abstract class Test {\n protected void doTest() {\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NoopMethodInAbstractClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OverridableMethodCallDuringObjectConstruction", + "shortDescription": { + "text": "Overridable method called during object construction" + }, + "fullDescription": { + "text": "Reports calls to overridable methods of the current class during object construction. A method is called during object construction if it is inside a: Constructor Non-static instance initializer Non-static field initializer 'clone()' method 'readObject()' method 'readObjectNoData()' method Methods are overridable if they are not declared as 'final', 'static', or 'private'. Package-local methods are considered safe, even though they are overridable. Such calls may result in subtle bugs, as object initialization may happen before the method call. Example: 'class Parent {\n void someMethod() { }\n }\n\n class Child extends Parent {\n Child() {\n someMethod();\n }\n }' This inspection shares the functionality with the following inspections: Abstract method called during object construction Overridden method called during object construction Only one inspection should be enabled at once to prevent warning duplication.", + "markdown": "Reports calls to overridable methods of the current class during object construction.\n\nA method is called during object construction if it is inside a:\n\n* Constructor\n* Non-static instance initializer\n* Non-static field initializer\n* `clone()` method\n* `readObject()` method\n* `readObjectNoData()` method\n* Methods are overridable if they are not declared as `final`, `static`, or `private`. Package-local methods are considered safe, even though they are overridable. Such calls may result in subtle bugs, as object initialization may happen before the method call.\n* **Example:**\n\n\n class Parent {\n void someMethod() { }\n }\n\n class Child extends Parent {\n Child() {\n someMethod();\n }\n }\n\n* This inspection shares the functionality with the following inspections:\n * Abstract method called during object construction\n * Overridden method called during object construction\n* Only one inspection should be enabled at once to prevent warning duplication." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OverridableMethodCallDuringObjectConstruction", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Initialization", + "index": 28, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OverloadedMethodsWithSameNumberOfParameters", + "shortDescription": { + "text": "Overloaded methods with same number of parameters" + }, + "fullDescription": { + "text": "Reports methods that are declared in the same class, have the same name, and the same number of parameters. Such overloads cam be very confusing because it can be unclear which overload gets called. Example: 'class Main {\n public static void execute(Runnable r) {}\n public static void execute(RunnableFuture c) {}\n }' Use the option to ignore overloaded methods whose parameter types are definitely incompatible.", + "markdown": "Reports methods that are declared in the same class, have the same name, and the same number of parameters. Such overloads cam be very confusing because it can be unclear which overload gets called.\n\n**Example:**\n\n\n class Main {\n public static void execute(Runnable r) {}\n public static void execute(RunnableFuture c) {}\n }\n\n\nUse the option to ignore overloaded methods whose parameter types are definitely incompatible." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OverloadedMethodsWithSameNumberOfParameters", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions/Method", + "index": 88, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ParametersPerMethod", + "shortDescription": { + "text": "Method with too many parameters" + }, + "fullDescription": { + "text": "Reports methods whose number of parameters exceeds the specified maximum. Methods with too many parameters can be a good sign that a refactoring is necessary. Methods that have super methods are not reported. Use the Parameter limit field to specify the maximum allowed number of parameters for a method.", + "markdown": "Reports methods whose number of parameters exceeds the specified maximum. Methods with too many parameters can be a good sign that a refactoring is necessary.\n\nMethods that have super methods are not reported.\n\nUse the **Parameter limit** field to specify the maximum allowed number of parameters for a method." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MethodWithTooManyParameters", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Method metrics", + "index": 95, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OverlyLongLambda", + "shortDescription": { + "text": "Overly long lambda expression" + }, + "fullDescription": { + "text": "Reports lambda expressions whose number of statements exceeds the specified maximum. Lambda expressions that are too long may be confusing, and it is often better to extract the statements into a separate method. The following statements are not counted: empty statements (semicolons) block statements 'for' loop initialization statements, that is, 'int i = ...' within a 'for(int i = ...;...)' statement 'for' loop update statements, that is, 'i += 2' within a 'for(int i = ...;...; i += 2)' statement Use the Non-comment source statements limit field to specify the maximum allowed number of statements in a lambda expression.", + "markdown": "Reports lambda expressions whose number of statements exceeds the specified maximum.\n\nLambda expressions that are too long may be confusing, and it is often better to extract the statements into a separate method.\n\n\nThe following statements are not counted:\n\n* empty statements (semicolons)\n* block statements\n* `for` loop initialization statements, that is, `int i = ...` within a `for(int i = ...;...)` statement\n* `for` loop update statements, that is, `i += 2` within a `for(int i = ...;...; i += 2)` statement\n\nUse the **Non-comment source statements limit** field to specify the maximum allowed number of statements in a lambda expression." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OverlyLongLambda", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Method metrics", + "index": 95, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MissingDeprecatedAnnotation", + "shortDescription": { + "text": "Missing '@Deprecated' annotation" + }, + "fullDescription": { + "text": "Reports module declarations, classes, fields, or methods that have the '@deprecated' Javadoc tag but do not have the '@java.lang.Deprecated' annotation. Example: '/**\n * @deprecated use {@code example()} instead\n */\n void sample(){ }' After the quick-fix is applied: '/**\n * @deprecated use {@code example()} instead\n */\n @Deprecated\n void sample(){ }' This inspection reports only if the language level of the project or module is 5 or higher. Use the checkbox below to report members annotated with '@Deprecated' without an explanation in a Javadoc '@deprecated' tag.", + "markdown": "Reports module declarations, classes, fields, or methods that have the `@deprecated` Javadoc tag but do not have the `@java.lang.Deprecated` annotation.\n\n**Example:**\n\n\n /**\n * @deprecated use {@code example()} instead\n */\n void sample(){ }\n\nAfter the quick-fix is applied:\n\n\n /**\n * @deprecated use {@code example()} instead\n */\n @Deprecated\n void sample(){ }\n\nThis inspection reports only if the language level of the project or module is 5 or higher.\n\n\nUse the checkbox below to report members annotated with `@Deprecated` without\nan explanation in a Javadoc `@deprecated` tag." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MissingDeprecatedAnnotation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Javadoc", + "index": 45, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ImplicitCallToSuper", + "shortDescription": { + "text": "Implicit call to 'super()'" + }, + "fullDescription": { + "text": "Reports constructors that do not begin with a call to \"super\" constructor or another constructor of the same class. Such constructors can be thought of as implicitly beginning with a call to 'super()'. Some coding standards prefer that such calls to 'super()' be made explicitly. Example: 'class Foo {\n Foo() {}\n }' After the quick-fix is applied: 'class Foo {\n Foo() {\n super();\n }\n }' Use the inspection settings to ignore classes extending directly from 'Object'. For instance: 'class Foo {\n Foo() {} // Not reported\n }\n\n class Bar extends Foo {\n Bar() {} // Implicit call to 'super()'\n }'", + "markdown": "Reports constructors that do not begin with a call to \"super\" constructor or another constructor of the same class.\n\nSuch constructors can be thought of as implicitly beginning with a\ncall to `super()`. Some coding standards prefer that such calls to\n`super()` be made explicitly.\n\n**Example:**\n\n\n class Foo {\n Foo() {}\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo {\n Foo() {\n super();\n }\n }\n\n\nUse the inspection settings to ignore classes extending directly from `Object`.\nFor instance:\n\n\n class Foo {\n Foo() {} // Not reported\n }\n\n class Bar extends Foo {\n Bar() {} // Implicit call to 'super()'\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ImplicitCallToSuper", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringTemplateMigration", + "shortDescription": { + "text": "String template can be used" + }, + "fullDescription": { + "text": "Reports 'String' concatenations that can be simplified by replacing them with the string template. Example: 'String name = \"Bob\";\n String greeting = \"Hello, \" + name + \". You are \" + 29 + \" years old.\";' After the quick-fix is applied: 'String name = \"Bob\";\n String greeting = STR.\"Hello, \\{name}. You are 29 years old.\";' This inspection only reports if the language level of the project or module is 21 or higher. New in 2023.3", + "markdown": "Reports `String` concatenations that can be simplified by replacing them with the string template.\n\n**Example:**\n\n\n String name = \"Bob\";\n String greeting = \"Hello, \" + name + \". You are \" + 29 + \" years old.\";\n\nAfter the quick-fix is applied:\n\n\n String name = \"Bob\";\n String greeting = STR.\"Hello, \\{name}. You are 29 years old.\";\n\nThis inspection only reports if the language level of the project or module is 21 or higher.\n\nNew in 2023.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StringTemplateMigration", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 21", + "index": 103, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MustAlreadyBeRemovedApi", + "shortDescription": { + "text": "API must already be removed" + }, + "fullDescription": { + "text": "Reports declarations marked with '@ApiStatus.ScheduledForRemoval' that should have been removed in the current version of the declaring library. It compares the specified scheduled removal version with the version that you can set below. Specify the version as a string separated with dots and optionally postfixed with 'alpha', 'beta', 'snapshot', or 'eap'. Examples of valid versions: '1.0', '2.3.1', '2018.1', '7.5-snapshot', '3.0-eap'. Version comparison is intuitive: '1.0 < 2.0', '1.0-eap < 1.0', '2.3-snapshot < 2.3' and so on. For detailed comparison logic, refer to the implementation of VersionComparatorUtil.", + "markdown": "Reports declarations marked with `@ApiStatus.ScheduledForRemoval` that should have been removed in the current version of the declaring library.\n\nIt compares the specified scheduled removal version with the version that you can set below.\n\n\nSpecify the version as a string separated with dots and optionally postfixed with\n`alpha`, `beta`, `snapshot`, or `eap`.\n\nExamples of valid versions: `1.0`, `2.3.1`, `2018.1`, `7.5-snapshot`, `3.0-eap`.\n\n\nVersion comparison is intuitive: `1.0 < 2.0`, `1.0-eap < 1.0`, `2.3-snapshot < 2.3` and so on.\nFor detailed comparison logic, refer to the implementation of [VersionComparatorUtil](https://github.com/JetBrains/intellij-community/blob/master/platform/util-rt/src/com/intellij/util/text/VersionComparatorUtil.java)." + }, + "defaultConfiguration": { + "enabled": true, + "level": "error", + "parameters": { + "suppressToolId": "MustAlreadyBeRemovedApi", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages", + "index": 2, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CachedNumberConstructorCall", + "shortDescription": { + "text": "Number constructor call with primitive argument" + }, + "fullDescription": { + "text": "Reports instantiations of new 'Long', 'Integer', 'Short', or 'Byte' objects that have a primitive 'long', 'integer', 'short', or 'byte' argument. It is recommended that you use the static method 'valueOf()' introduced in Java 5. By default, this method caches objects for values between -128 and 127 inclusive. Example: 'Integer i = new Integer(1);\n Long l = new Long(1L);' After the quick-fix is applied, the code changes to: 'Integer i = Integer.valueOf(1);\n Long l = Long.valueOf(1L);' This inspection only reports if the language level of the project or module is 5 or higher Use the Ignore new number expressions with a String argument option to ignore calls to number constructors with a 'String' argument. Use the Report only when constructor is @Deprecated option to only report calls to deprecated constructors. 'Long', 'Integer', 'Short' and 'Byte' constructors are deprecated since JDK 9.", + "markdown": "Reports instantiations of new `Long`, `Integer`, `Short`, or `Byte` objects that have a primitive `long`, `integer`, `short`, or `byte` argument.\n\nIt is recommended that you use the static method `valueOf()`\nintroduced in Java 5. By default, this method caches objects for values between -128 and\n127 inclusive.\n\n**Example:**\n\n\n Integer i = new Integer(1);\n Long l = new Long(1L);\n\nAfter the quick-fix is applied, the code changes to:\n\n\n Integer i = Integer.valueOf(1);\n Long l = Long.valueOf(1L);\n\nThis inspection only reports if the language level of the project or module is 5 or higher\n\n\nUse the **Ignore new number expressions with a String argument** option to ignore calls to number constructors with a `String` argument.\n\n\nUse the **Report only when constructor is @Deprecated** option to only report calls to deprecated constructors.\n`Long`, `Integer`, `Short` and `Byte` constructors are deprecated since JDK 9." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "CachedNumberConstructorCall", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CloneDeclaresCloneNotSupported", + "shortDescription": { + "text": "'clone()' does not declare 'CloneNotSupportedException'" + }, + "fullDescription": { + "text": "Reports 'clone()' methods that do not declare 'throws CloneNotSupportedException'. If 'throws CloneNotSupportedException' is not declared, the method's subclasses will not be able to prohibit cloning in the standard way. This inspection does not report 'clone()' methods declared 'final' and 'clone()' methods on 'final' classes. Configure the inspection: Use the Only warn on 'protected' clone methods option to indicate that this inspection should only warn on 'protected clone()' methods. The Effective Java book (second and third edition) recommends omitting the 'CloneNotSupportedException' declaration on 'public' methods, because the methods that do not throw checked exceptions are easier to use. Example: 'public class Example implements Cloneable {\n // method doesn't declare 'throws CloneNotSupportedException'\n protected Object clone() {\n try {\n return super.clone();\n } catch (CloneNotSupportedException e) {\n return null;\n }\n }\n }'", + "markdown": "Reports `clone()` methods that do not declare `throws CloneNotSupportedException`.\n\nIf `throws CloneNotSupportedException` is not declared, the method's subclasses will not be able to prohibit cloning\nin the standard way. This inspection does not report `clone()` methods declared `final`\nand `clone()` methods on `final` classes.\n\nConfigure the inspection:\n\nUse the **Only warn on 'protected' clone methods** option to indicate that this inspection should only warn on `protected clone()` methods.\nThe *Effective Java* book (second and third edition) recommends omitting the `CloneNotSupportedException`\ndeclaration on `public` methods, because the methods that do not throw checked exceptions are easier to use.\n\nExample:\n\n\n public class Example implements Cloneable {\n // method doesn't declare 'throws CloneNotSupportedException'\n protected Object clone() {\n try {\n return super.clone();\n } catch (CloneNotSupportedException e) {\n return null;\n }\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "CloneDoesntDeclareCloneNotSupportedException", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Cloning issues", + "index": 82, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BooleanExpressionMayBeConditional", + "shortDescription": { + "text": "Boolean expression could be replaced with conditional expression" + }, + "fullDescription": { + "text": "Reports any 'boolean' expressions which can be formulated in a more compact and, arguably, clear way than by using a conditional expression. Use the quick-fix to replace the 'boolean' expression by a conditional expression. Example: 'a && b || !a && c;' After the quick-fix is applied: 'a ? b : c;'", + "markdown": "Reports any `boolean` expressions which can be formulated in a more compact and, arguably, clear way than by using a conditional expression.\n\nUse the quick-fix to replace the `boolean` expression by a conditional expression.\n\n**Example:**\n\n\n a && b || !a && c;\n\nAfter the quick-fix is applied:\n\n\n a ? b : c;\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "BooleanExpressionMayBeConditional", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FieldHidesSuperclassField", + "shortDescription": { + "text": "Subclass field hides superclass field" + }, + "fullDescription": { + "text": "Reports fields in a derived class that are named identically a field of a superclass. Java fields cannot be overridden in derived classes, so the field in the derived class will hide the field from the superclass. As a result of such naming, you may accidentally use the field of the derived class where the identically named field of a base class is intended. A quick-fix is suggested to rename the field in the derived class. Example: 'class Parent {\n Parent parent;\n}\nclass Child extends Parent {\n Child parent;\n}' You can configure the following options for this inspection: Ignore non-accessible fields - indicates whether this inspection should report all name clashes, or only clashes with fields which are visible from the subclass. Ignore static fields hiding static fields - ignore 'static' fields which hide 'static' fields in base classes.", + "markdown": "Reports fields in a derived class that are named identically a field of a superclass. Java fields cannot be overridden in derived classes, so the field in the derived class will hide the field from the superclass.\n\n\nAs a result of such naming, you may accidentally use the field of the derived class\nwhere the identically named field of a base class is intended.\n\nA quick-fix is suggested to rename the field in the derived class.\n\n**Example:**\n\n class Parent {\n Parent parent;\n }\n class Child extends Parent {\n Child parent;\n }\n\n\nYou can configure the following options for this inspection:\n\n1. **Ignore non-accessible fields** - indicates whether this inspection should report all name clashes, or only clashes with fields which are visible from the subclass.\n2. **Ignore static fields hiding static fields** - ignore `static` fields which hide `static` fields in base classes." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "FieldNameHidesFieldInSuperclass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Visibility", + "index": 57, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryExplicitNumericCast", + "shortDescription": { + "text": "Unnecessary explicit numeric cast" + }, + "fullDescription": { + "text": "Reports primitive numeric casts that would be inserted implicitly by the compiler. Also, reports any primitive numeric casts that the compiler will remove. Example: 'int x = (short)5; // The cast will be removed by the javac tool' After the quick-fix is applied: 'int x = 5;'", + "markdown": "Reports primitive numeric casts that would be inserted implicitly by the compiler. Also, reports any primitive numeric casts that the compiler will remove.\n\n**Example:**\n\n int x = (short)5; // The cast will be removed by the javac tool\n\nAfter the quick-fix is applied:\n`int x = 5;`" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryExplicitNumericCast", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues/Cast", + "index": 102, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SimpleDateFormatWithoutLocale", + "shortDescription": { + "text": "'SimpleDateFormat' without locale" + }, + "fullDescription": { + "text": "Reports instantiations of 'java.util.SimpleDateFormat' or 'java.time.format.DateTimeFormatter' that do not specify a 'java.util.Locale'. These calls will use the platform default locale, which depends on the OS settings. This can lead to surprising behaviour when the code is run on a different platform or the OS settings are changed. 'Example:' 'new SimpleDateFormat(\"yyyy\");\n DateTimeFormatter.ofPattern(\"d/M/y\");'", + "markdown": "Reports instantiations of `java.util.SimpleDateFormat` or `java.time.format.DateTimeFormatter` that do not specify a `java.util.Locale`. These calls will use the platform default locale, which depends on the OS settings. This can lead to surprising behaviour when the code is run on a different platform or the OS settings are changed.\n\n`Example:`\n\n\n new SimpleDateFormat(\"yyyy\");\n DateTimeFormatter.ofPattern(\"d/M/y\");\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SimpleDateFormatWithoutLocale", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Internationalization", + "index": 9, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AnnotationClass", + "shortDescription": { + "text": "Annotation interface" + }, + "fullDescription": { + "text": "Reports annotation interfaces. Such interfaces are not supported under Java 1.4 and earlier.", + "markdown": "Reports annotation interfaces. Such interfaces are not supported under Java 1.4 and earlier." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AnnotationClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level issues", + "index": 64, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TransientFieldNotInitialized", + "shortDescription": { + "text": "Transient field is not initialized on deserialization" + }, + "fullDescription": { + "text": "Reports 'transient' fields that are initialized during normal object construction, but whose class does not have a 'readObject' method. As 'transient' fields are not serialized they need to be initialized separately in a 'readObject()' method during deserialization. Any 'transient' fields that are not initialized during normal object construction are considered to use the default initialization and are not reported by this inspection. Example: 'class Person implements Serializable {\n transient String name = \"Default\"; //warning, can actually be a null after deserialization\n transient String surname; //null is considered the default value and not reported\n }'", + "markdown": "Reports `transient` fields that are initialized during normal object construction, but whose class does not have a `readObject` method.\n\n\nAs `transient` fields are not serialized they need\nto be initialized separately in a `readObject()` method\nduring deserialization.\n\n\nAny `transient` fields that\nare not initialized during normal object construction are considered to use the default\ninitialization and are not reported by this inspection.\n\n**Example:**\n\n\n class Person implements Serializable {\n transient String name = \"Default\"; //warning, can actually be a null after deserialization\n transient String surname; //null is considered the default value and not reported\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "TransientFieldNotInitialized", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PropertyValueSetToItself", + "shortDescription": { + "text": "Property value set to itself" + }, + "fullDescription": { + "text": "Reports calls of setter methods with the same object getter as a value. Usually, this code does nothing and probably was not intended. For example: 'bean.setPayerId(bean.getPayerId());'", + "markdown": "Reports calls of setter methods with the same object getter as a value. Usually, this code does nothing and probably was not intended.\n\n**For example:**\n\n bean.setPayerId(bean.getPayerId());\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PropertyValueSetToItself", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/JavaBeans issues", + "index": 33, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Finalize", + "shortDescription": { + "text": "'finalize()' should not be overridden" + }, + "fullDescription": { + "text": "Reports overriding the 'Object.finalize()' method. According to the 'Object.finalize()' documentation: The finalization mechanism is inherently problematic. Finalization can lead to performance issues, deadlocks, and hangs. Errors in finalizers can lead to resource leaks; there is no way to cancel finalization if it is no longer necessary; and no ordering is specified among calls to 'finalize' methods of different objects. Furthermore, there are no guarantees regarding the timing of finalization. The 'finalize' method might be called on a finalizable object only after an indefinite delay, if at all. Configure the inspection: Use the Ignore for trivial 'finalize()' implementations option to ignore 'finalize()' implementations with an empty method body or a body containing only 'if' statements that have a condition which evaluates to 'false' and is a compile-time constant. For performance reasons it can be beneficial to override a non-trivial 'finalize()' with an empty implementation in a subclass. An empty final 'finalize()' implementation can also be used to prevent subclasses from overriding.", + "markdown": "Reports overriding the `Object.finalize()` method.\n\nAccording to the `Object.finalize()` documentation:\n>\n> The finalization mechanism is inherently problematic. Finalization can lead\n> to performance issues, deadlocks, and hangs. Errors in finalizers can lead\n> to resource leaks; there is no way to cancel finalization if it is no longer\n> necessary; and no ordering is specified among calls to `finalize`\n> methods of different objects. Furthermore, there are no guarantees regarding\n> the timing of finalization. The `finalize` method might be called\n> on a finalizable object only after an indefinite delay, if at all.\n\nConfigure the inspection:\n\n* Use the **Ignore for trivial 'finalize()' implementations** option to ignore `finalize()` implementations with an empty method body or a body containing only `if` statements that have a condition which evaluates to `false` and is a compile-time constant. For performance reasons it can be beneficial to override a non-trivial `finalize()` with an empty implementation in a subclass. An empty final `finalize()` implementation can also be used to prevent subclasses from overriding." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "FinalizeDeclaration", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Finalization", + "index": 66, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JUnit3StyleTestMethodInJUnit4Class", + "shortDescription": { + "text": "Old style JUnit test method in JUnit 4 class" + }, + "fullDescription": { + "text": "Reports JUnit 3 style test methods that are located inside a class that does not extend the JUnit 3 'TestCase' class and contains JUnit 4 or JUnit 5 '@Test' annotated methods. Such test methods cannot be run.", + "markdown": "Reports JUnit 3 style test methods that are located inside a class that does not extend the JUnit 3 `TestCase` class and contains JUnit 4 or JUnit 5 `@Test` annotated methods. Such test methods cannot be run." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "JUnit3StyleTestMethodInJUnit4Class", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/JUnit", + "index": 77, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PointlessArithmeticExpression", + "shortDescription": { + "text": "Pointless arithmetic expression" + }, + "fullDescription": { + "text": "Reports pointless arithmetic expressions. Such expressions include adding or subtracting zero, multiplying by zero or one, and division by one. Such expressions may be the result of automated refactorings and they are unlikely to be what the developer intended to do. The quick-fix simplifies such expressions. Example: 'void f(int a) {\n int x = a - a;\n int y = a + 0;\n int res = x / x;\n }' After the quick-fix is applied: 'void f(int a) {\n int x = 0;\n int y = a;\n int res = 1;\n }' Note that in rare cases, the suggested replacement might not be completely equivalent to the original code for all possible inputs. For example, the inspection suggests replacing 'x / x' with '1'. However, if 'x' is zero, the original code throws 'ArithmeticException' or results in 'NaN'. Also, if 'x' is 'NaN', then the result is also 'NaN'. It's very unlikely that such behavior is intended.", + "markdown": "Reports pointless arithmetic expressions. Such expressions include adding or subtracting zero, multiplying by zero or one, and division by one.\n\nSuch expressions may be the result of automated refactorings and they are unlikely to be what the developer intended to do.\n\nThe quick-fix simplifies such expressions.\n\n**Example:**\n\n\n void f(int a) {\n int x = a - a;\n int y = a + 0;\n int res = x / x;\n }\n\nAfter the quick-fix is applied:\n\n\n void f(int a) {\n int x = 0;\n int y = a;\n int res = 1;\n }\n\n\nNote that in rare cases, the suggested replacement might not be completely equivalent to the original code\nfor all possible inputs. For example, the inspection suggests replacing `x / x` with `1`.\nHowever, if `x` is zero, the original code throws `ArithmeticException` or results in `NaN`.\nAlso, if `x` is `NaN`, then the result is also `NaN`. It's very unlikely that such behavior is intended." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "PointlessArithmeticExpression", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OverlyLargePrimitiveArrayInitializer", + "shortDescription": { + "text": "Overly large initializer for array of primitive type" + }, + "fullDescription": { + "text": "Reports array initializer expressions for primitive arrays that contain too many elements. Such initializers may result in overly large class files because code must be generated to initialize each array element. In memory or bandwidth constrained environments, it may be more efficient to load large arrays of primitives from resource files. This inspection is intended for Java ME and other highly resource constrained environments. Applying the results of this inspection without consideration might have negative effects on code clarity and design. Use the option to specify the maximum number of elements to allow in primitive array initializers.", + "markdown": "Reports array initializer expressions for primitive arrays that contain too many elements. Such initializers may result in overly large class files because code must be generated to initialize each array element. In memory or bandwidth constrained environments, it may be more efficient to load large arrays of primitives from resource files.\n\n\nThis inspection is intended for Java ME and other highly resource constrained environments.\nApplying the results of this inspection without consideration might have negative effects on code clarity and design.\n\n\nUse the option to specify the maximum number of elements to allow in primitive array initializers." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OverlyLargePrimitiveArrayInitializer", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance/Embedded", + "index": 19, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassInitializer", + "shortDescription": { + "text": "Non-'static' initializer" + }, + "fullDescription": { + "text": "Reports non-'static' initializers in classes. Some coding standards prohibit instance initializers and recommend using constructors or field initializers for initialization. Also, deleting the 'static' keyword may accidentally create non-'static' initializers and result in obscure bugs. This inspection doesn't report instance initializers in anonymous classes. Use the Only warn when the class has one or more constructors option to ignore instance initializers in classes that don't have any constructors.", + "markdown": "Reports non-`static` initializers in classes.\n\nSome coding standards prohibit instance initializers and recommend using constructors or field initializers for initialization.\nAlso, deleting the `static` keyword may accidentally create non-`static` initializers and result in obscure bugs.\n\nThis inspection doesn't report instance initializers in anonymous classes.\n\n\nUse the **Only warn when the class has one or more constructors** option to ignore instance initializers in classes that don't have any constructors." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonStaticInitializer", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ObjectNotify", + "shortDescription": { + "text": "Call to 'notify()' instead of 'notifyAll()'" + }, + "fullDescription": { + "text": "Reports calls to 'Object.notify()'. While occasionally useful, in almost all cases 'Object.notifyAll()' is a better choice because calling 'Object.notify()' may lead to deadlocks. See Doug Lea's Concurrent Programming in Java for a discussion.", + "markdown": "Reports calls to `Object.notify()`. While occasionally useful, in almost all cases `Object.notifyAll()` is a better choice because calling `Object.notify()` may lead to deadlocks. See Doug Lea's *Concurrent Programming in Java* for a discussion." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToNotifyInsteadOfNotifyAll", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ContinueOrBreakFromFinallyBlock", + "shortDescription": { + "text": "'continue' or 'break' inside 'finally' block" + }, + "fullDescription": { + "text": "Reports 'break' or 'continue' statements inside of 'finally' blocks. While occasionally intended, such statements are very confusing, may mask thrown exceptions, and complicate debugging. Example: 'while (true) {\n try {\n throwingMethod();\n } finally {\n continue;\n }\n }'", + "markdown": "Reports `break` or `continue` statements inside of `finally` blocks.\n\nWhile occasionally intended, such statements are very confusing, may mask thrown exceptions, and complicate debugging.\n\n**Example:**\n\n\n while (true) {\n try {\n throwingMethod();\n } finally {\n continue;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ContinueOrBreakFromFinallyBlock", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LengthOneStringsInConcatenation", + "shortDescription": { + "text": "Single character string concatenation" + }, + "fullDescription": { + "text": "Reports concatenation with string literals that consist of one character. These literals may be replaced with equivalent character literals, gaining some performance enhancement. Example: 'String hello = hell + \"o\";' After the quick-fix is applied: 'String hello = hell + 'o';'", + "markdown": "Reports concatenation with string literals that consist of one character.\n\nThese literals may be replaced with equivalent character literals, gaining some performance enhancement.\n\n**Example:**\n\n\n String hello = hell + \"o\";\n\nAfter the quick-fix is applied:\n\n\n String hello = hell + 'o';\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "SingleCharacterStringConcatenation", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryThis", + "shortDescription": { + "text": "Unnecessary 'this' qualifier" + }, + "fullDescription": { + "text": "Reports unnecessary 'this' qualifier. Using 'this' to disambiguate a code reference is discouraged by many coding styles and may easily become unnecessary via automatic refactorings. Example: 'class Foo {\n int x;\n void foo() {\n this.x = 2;\n }\n }' After the quick-fix is applied: 'class Foo {\n int x;\n void foo() {\n x = 2;\n }\n }' Use the inspection settings to ignore assignments to fields. For instance, 'this.x = 2;' won't be reported, but 'int y = this.x;' will be.", + "markdown": "Reports unnecessary `this` qualifier.\n\n\nUsing `this` to disambiguate a code reference is discouraged by many coding styles\nand may easily become unnecessary\nvia automatic refactorings.\n\n**Example:**\n\n\n class Foo {\n int x;\n void foo() {\n this.x = 2;\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo {\n int x;\n void foo() {\n x = 2;\n }\n }\n\n\nUse the inspection settings to ignore assignments to fields.\nFor instance, `this.x = 2;` won't be reported, but `int y = this.x;` will be." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryThis", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassWithTooManyTransitiveDependencies", + "shortDescription": { + "text": "Class with too many transitive dependencies" + }, + "fullDescription": { + "text": "Reports classes that are directly or indirectly dependent on too many other classes. Modifications to any dependency of such a class may require changing the class thus making it prone to instability. Only top-level classes are reported. Use the Maximum number of transitive dependencies field to specify the maximum allowed number of direct or indirect dependencies for a class. Available only from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name and isn't reported in the editor.", + "markdown": "Reports classes that are directly or indirectly dependent on too many other classes.\n\nModifications to any dependency of such a class may require changing the class thus making it prone to instability.\n\nOnly top-level classes are reported.\n\nUse the **Maximum number of transitive dependencies** field to specify the maximum allowed number of direct or indirect dependencies\nfor a class.\n\nAvailable only from **Code \\| Inspect Code** or\n**Code \\| Analyze Code \\| Run Inspection by Name** and isn't reported in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassWithTooManyTransitiveDependencies", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Dependency issues", + "index": 89, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InstanceVariableUninitializedUse", + "shortDescription": { + "text": "Instance field used before initialization" + }, + "fullDescription": { + "text": "Reports instance variables that are read before initialization. The inspection ignores equality checks with 'null'. Example: 'class Foo {\n int bar;\n\n Foo() {\n System.out.println(bar);\n }\n }' Note that this inspection uses a very conservative dataflow algorithm and may incorrectly report instance variables as uninitialized. Variables reported as initialized will always be initialized. Use the Ignore if annotated by option to specify special annotations. The inspection will ignore fields annotated with one of these annotations. Use the Ignore primitive fields option to ignore uninitialized primitive fields.", + "markdown": "Reports instance variables that are read before initialization.\n\nThe inspection ignores equality checks with `null`.\n\n**Example:**\n\n\n class Foo {\n int bar;\n\n Foo() {\n System.out.println(bar);\n }\n }\n\nNote that this inspection uses a very conservative dataflow algorithm and may incorrectly report instance variables as uninitialized. Variables\nreported as initialized will always be initialized.\n\nUse the **Ignore if annotated by** option to specify special annotations. The inspection will ignore fields\nannotated with one of these annotations.\n\nUse the **Ignore primitive fields** option to ignore uninitialized primitive fields." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "InstanceVariableUsedBeforeInitialized", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Initialization", + "index": 28, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ExplicitArrayFilling", + "shortDescription": { + "text": "Explicit array filling" + }, + "fullDescription": { + "text": "Reports loops that can be replaced with 'Arrays.setAll()' or 'Arrays.fill()' calls. This inspection suggests replacing loops with 'Arrays.setAll()' if the language level of the project or module is 8 or higher. Replacing loops with 'Arrays.fill()' is possible with any language level. Example: 'for (int i=0; i list, int from, int to) {\n for (int i = from; i < to; i++) {\n list.remove(from);\n }\n }' After the quick-fix is applied: 'void removeRange(List list, int from, int to) {\n if (to > from) {\n list.subList(from, to).clear();\n }\n }' The quick-fix adds a range check automatically to prevent a possible 'IndexOutOfBoundsException' when the minimal value is bigger than the maximal value. It can be removed if such a situation is impossible in your code. New in 2018.2", + "markdown": "Reports `List.remove(index)` called in a loop that can be replaced with `List.subList().clear()`.\n\nThe replacement\nis more efficient for most `List` implementations when many elements are deleted.\n\nExample:\n\n\n void removeRange(List list, int from, int to) {\n for (int i = from; i < to; i++) {\n list.remove(from);\n }\n }\n\nAfter the quick-fix is applied:\n\n\n void removeRange(List list, int from, int to) {\n if (to > from) {\n list.subList(from, to).clear();\n }\n }\n\n\nThe quick-fix adds a range check automatically to prevent a possible `IndexOutOfBoundsException` when the minimal value is bigger\nthan the maximal value. It can be removed if such a situation is impossible in your code.\n\nNew in 2018.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ListRemoveInLoop", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReturnFromFinallyBlock", + "shortDescription": { + "text": "'return' inside 'finally' block" + }, + "fullDescription": { + "text": "Reports 'return' statements inside of 'finally' blocks. While occasionally intended, such 'return' statements may mask thrown exceptions and complicate debugging. Example: 'try {\n foo();\n } finally {\n if (bar()) return;\n }'", + "markdown": "Reports `return` statements inside of `finally` blocks.\n\nWhile occasionally intended, such `return` statements may mask thrown exceptions\nand complicate debugging.\n\n**Example:**\n\n\n try {\n foo();\n } finally {\n if (bar()) return;\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ReturnInsideFinallyBlock", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MissingOverrideAnnotation", + "shortDescription": { + "text": "Missing '@Override' annotation" + }, + "fullDescription": { + "text": "Reports methods overriding superclass methods but are not annotated with '@java.lang.Override'. Annotating methods with '@java.lang.Override' improves code readability since it shows the intent. In addition, the compiler emits an error when a signature of the overridden method doesn't match the superclass method. Example: 'class X {\n public String toString() {\n return \"hello world\";\n }\n }' After the quick-fix is applied: 'class X {\n @Override\n public String toString() {\n return \"hello world\";\n }\n }' Configure the inspection: Use the Ignore 'equals()', 'hashCode()' and 'toString()' option to ignore these 'java.lang.Object' methods: 'equals()', 'hashCode()', and 'toString()'. The risk that these methods will disappear and your code won't be compiling anymore due to the '@Override' annotation is relatively small. Use the Ignore methods in anonymous classes option to ignore methods in anonymous classes. Disable the Highlight method when its overriding methods do not all have the '@Override' annotation option to only warn on the methods missing an '@Override' annotation, and not on overridden methods where one or more descendants are missing an '@Override' annotation. This inspection only reports if the language level of the project or module is 5 or higher.", + "markdown": "Reports methods overriding superclass methods but are not annotated with `@java.lang.Override`.\n\n\nAnnotating methods with `@java.lang.Override` improves code readability since it shows the intent.\nIn addition, the compiler emits an error when a signature of the overridden method doesn't match the superclass method.\n\n**Example:**\n\n\n class X {\n public String toString() {\n return \"hello world\";\n }\n }\n \nAfter the quick-fix is applied:\n\n\n class X {\n @Override\n public String toString() {\n return \"hello world\";\n }\n }\n \nConfigure the inspection:\n\n* Use the **Ignore 'equals()', 'hashCode()' and 'toString()'** option to ignore these `java.lang.Object` methods: `equals()`, `hashCode()`, and `toString()`. The risk that these methods will disappear and your code won't be compiling anymore due to the `@Override` annotation is relatively small.\n* Use the **Ignore methods in anonymous classes** option to ignore methods in anonymous classes.\n* Disable the **Highlight method when its overriding methods do not all have the '@Override' annotation** option to only warn on the methods missing an `@Override` annotation, and not on overridden methods where one or more descendants are missing an `@Override` annotation.\n\nThis inspection only reports if the language level of the project or module is 5 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "override", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Inheritance issues", + "index": 25, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantExplicitClose", + "shortDescription": { + "text": "Redundant 'close()'" + }, + "fullDescription": { + "text": "Reports unnecessary calls to 'close()' at the end of a try-with-resources block and suggests removing them. Example: 'try(MyAutoCloseable ac = new MyAutoCloseable()) {\n foo();\n ac.close();\n }' After the quick-fix is applied: 'try(MyAutoCloseable ac = new MyAutoCloseable()) {\n foo();\n }' New in 2018.1", + "markdown": "Reports unnecessary calls to `close()` at the end of a try-with-resources block and suggests removing them.\n\n**Example**:\n\n\n try(MyAutoCloseable ac = new MyAutoCloseable()) {\n foo();\n ac.close();\n }\n\nAfter the quick-fix is applied:\n\n\n try(MyAutoCloseable ac = new MyAutoCloseable()) {\n foo();\n }\n\nNew in 2018.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantExplicitClose", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AssertStatement", + "shortDescription": { + "text": "'assert' statement" + }, + "fullDescription": { + "text": "Reports 'assert' statements. By default, 'assert' statements are disabled during execution in the production environment. Consider using logger or exceptions instead. The 'assert' statements are not supported in Java 1.3 and earlier JVM.", + "markdown": "Reports `assert` statements. By default, `assert` statements are disabled during execution in the production environment. Consider using logger or exceptions instead.\n\nThe `assert` statements are not supported in Java 1.3 and earlier JVM." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AssertStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level issues", + "index": 64, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CharUsedInArithmeticContext", + "shortDescription": { + "text": "'char' expression used in arithmetic context" + }, + "fullDescription": { + "text": "Reports expressions of the 'char' type used in addition or subtraction expressions. Such code is not necessarily an issue but may result in bugs (for example, if a string is expected). Example: 'int a = 'a' + 42;' After the quick-fix is applied: 'int a = (int) 'a' + 42;' For the 'String' context: 'int i1 = 1;\nint i2 = 2;\nSystem.out.println(i2 + '-' + i1 + \" = \" + (i2 - i1));' After the quick-fix is applied: 'System.out.println(i2 + \"-\" + i1 + \" = \" + (i2 - i1));'", + "markdown": "Reports expressions of the `char` type used in addition or subtraction expressions.\n\nSuch code is not necessarily an issue but may result in bugs (for example,\nif a string is expected).\n\n**Example:** `int a = 'a' + 42;`\n\nAfter the quick-fix is applied: `int a = (int) 'a' + 42;`\n\nFor the `String` context:\n\n int i1 = 1;\n int i2 = 2;\n System.out.println(i2 + '-' + i1 + \" = \" + (i2 - i1));\n\nAfter the quick-fix is applied:\n`System.out.println(i2 + \"-\" + i1 + \" = \" + (i2 - i1));`" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CharUsedInArithmeticContext", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringEqualsCharSequence", + "shortDescription": { + "text": "'String.equals()' called with 'CharSequence' argument" + }, + "fullDescription": { + "text": "Reports calls to 'String.equals()' with a 'CharSequence' as the argument. 'String.equals()' can only return 'true' for 'String' arguments. To compare the contents of a 'String' with a non-'String' 'CharSequence' argument, use the 'contentEquals()' method. Example: 'boolean equals(String s, CharSequence ch) {\n return s.equals(ch);\n }' After quick-fix is applied: 'boolean equals(String s, CharSequence ch) {\n return s.contentEquals(ch);\n }' New in 2017.3", + "markdown": "Reports calls to `String.equals()` with a `CharSequence` as the argument.\n\n\n`String.equals()` can only return `true` for `String` arguments.\nTo compare the contents of a `String` with a non-`String` `CharSequence` argument,\nuse the `contentEquals()` method.\n\n**Example:**\n\n\n boolean equals(String s, CharSequence ch) {\n return s.equals(ch);\n }\n\nAfter quick-fix is applied:\n\n\n boolean equals(String s, CharSequence ch) {\n return s.contentEquals(ch);\n }\n\n\nNew in 2017.3" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "StringEqualsCharSequence", + "cweIds": [ + 597 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TestFailedLine", + "shortDescription": { + "text": "Failed line in test" + }, + "fullDescription": { + "text": "Reports failed method calls or assertions in tests. It helps detect the failed line in code faster and start debugging it immediately. Example: '@Test\n fun foo() {\n assertEquals(1, 0) // highlighted\n }'", + "markdown": "Reports failed method calls or assertions in tests. It helps detect the failed line in code faster and start debugging it immediately.\n\n**Example:**\n\n\n @Test\n fun foo() {\n assertEquals(1, 0) // highlighted\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "TestFailedLine", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages/Test frameworks", + "index": 94, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessarySemicolon", + "shortDescription": { + "text": "Unnecessary semicolon" + }, + "fullDescription": { + "text": "Reports any unnecessary semicolons, including semicolons that are used between class members, inside block statements, or after class definitions. Even though these semicolons are valid in Java, they are redundant and may be removed. Example: 'class C {\n ;\n void m() throws Exception {\n try (AutoCloseable r1 = createAutoCloseable();) {\n ;\n }\n }\n ;\n }' After the quick-fix is applied: 'class C {\n void m() throws Exception {\n try (AutoCloseable r1 = createAutoCloseable()) {\n }\n }\n }'", + "markdown": "Reports any unnecessary semicolons, including semicolons that are used between class members, inside block statements, or after class definitions.\n\nEven though these semicolons are valid in Java, they are redundant and may be removed.\n\nExample:\n\n\n class C {\n ;\n void m() throws Exception {\n try (AutoCloseable r1 = createAutoCloseable();) {\n ;\n }\n }\n ;\n }\n\nAfter the quick-fix is applied:\n\n\n class C {\n void m() throws Exception {\n try (AutoCloseable r1 = createAutoCloseable()) {\n }\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessarySemicolon", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AssertEqualsMayBeAssertSame", + "shortDescription": { + "text": "'assertEquals()' may be 'assertSame()'" + }, + "fullDescription": { + "text": "Reports JUnit 'assertEquals()' calls that can be replaced with an equivalent 'assertSame()' call. This is possible when the arguments are instances of a 'final' class that does not override the 'Object.equals()' method and makes it explicit that the object identity is compared. Suggests replacing 'assertEquals()' with 'assertSame()'. Example: '@Test\n public void testObjectType() {\n Object o = getObject();\n Assert.assertEquals(String.class, o.getClass());\n }' After the quick fix is applied: '@Test\n public void testSort() {\n Object o = getObject();\n Assert.assertSame(String.class, o.getClass());\n }'", + "markdown": "Reports JUnit `assertEquals()` calls that can be replaced with an equivalent `assertSame()` call. This is possible when the arguments are instances of a `final` class that does not override the `Object.equals()` method and makes it explicit that the object identity is compared.\n\nSuggests replacing `assertEquals()` with `assertSame()`.\n\n**Example:**\n\n\n @Test\n public void testObjectType() {\n Object o = getObject();\n Assert.assertEquals(String.class, o.getClass());\n }\n\nAfter the quick fix is applied:\n\n\n @Test\n public void testSort() {\n Object o = getObject();\n Assert.assertSame(String.class, o.getClass());\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AssertEqualsMayBeAssertSame", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages/Test frameworks", + "index": 94, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PublicStaticCollectionField", + "shortDescription": { + "text": "'public static' collection field" + }, + "fullDescription": { + "text": "Reports modifiable 'public' 'static' Collection fields. Even though they are often used to store collections of constant values, these fields nonetheless represent a security hazard, as their contents may be modified even if the field is declared as 'final'. Example: 'public static final List EVENTS = new ArrayList<>();'\n Use the table in the Options section to specify methods returning unmodifiable collections. 'public' 'static' collection fields initialized with these methods will not be reported.", + "markdown": "Reports modifiable `public` `static` Collection fields.\n\nEven though they are often used to store collections of constant values, these fields nonetheless represent a security\nhazard, as their contents may be modified even if the field is declared as `final`.\n\n**Example:**\n\n\n public static final List EVENTS = new ArrayList<>();\n \n\nUse the table in the **Options** section to specify methods returning unmodifiable collections.\n`public` `static` collection fields initialized with these methods will not be reported." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PublicStaticCollectionField", + "cweIds": [ + 732 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Security", + "index": 30, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ExtendsConcreteCollection", + "shortDescription": { + "text": "Class explicitly extends a 'Collection' class" + }, + "fullDescription": { + "text": "Reports classes that extend concrete subclasses of the 'java.util.Collection' or 'java.util.Map' classes. Subclassing concrete collection types is a common yet poor practice. It is considerably more brittle than delegating collection calls.", + "markdown": "Reports classes that extend concrete subclasses of the `java.util.Collection` or `java.util.Map` classes.\n\n\nSubclassing concrete collection types is a common yet poor practice. It is considerably more brittle than delegating collection calls." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassExtendsConcreteCollection", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Inheritance issues", + "index": 25, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JavadocBlankLines", + "shortDescription": { + "text": "Blank line should be replaced with

to break lines" + }, + "fullDescription": { + "text": "Reports blank lines in Javadoc comments. Blank lines in Javadoc may signal an intention split the text to different paragraphs. However, the Javadoc tool and IntelliJ IDEA will ignore them when rendering documentation comments. The quick-fix suggests to replace the blank line with a paragraph tag (

). Example: 'class Main {\n /**\n * Doesn't do anything.\n *\n * Does absolutely nothing\n */\n void foo() {}\n }' After the quick-fix is applied: 'class Main {\n /**\n * Doesn't do anything.\n *

\n * Does absolutely nothing\n */\n void foo() {}\n }' New in 2022.1", + "markdown": "Reports blank lines in Javadoc comments.\n\n\nBlank lines in Javadoc may signal an intention split the text to different paragraphs. However, the Javadoc tool and IntelliJ IDEA will\nignore them when rendering documentation comments.\n\n\nThe quick-fix suggests to replace the blank line with a paragraph tag (\\).\n\n**Example:**\n\n\n class Main {\n /**\n * Doesn't do anything.\n *\n * Does absolutely nothing\n */\n void foo() {}\n }\n\nAfter the quick-fix is applied:\n\n\n class Main {\n /**\n * Doesn't do anything.\n *

\n * Does absolutely nothing\n */\n void foo() {}\n }\n\nNew in 2022.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "JavadocBlankLines", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Javadoc", + "index": 45, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ObjectInstantiationInEqualsHashCode", + "shortDescription": { + "text": "Object instantiation inside 'equals()' or 'hashCode()'" + }, + "fullDescription": { + "text": "Reports construction of (temporary) new objects inside 'equals()', 'hashCode()', 'compareTo()', and 'Comparator.compare()' methods. Besides constructor invocations, new objects can also be created by autoboxing or iterator creation inside a 'foreach' statement. This can cause performance problems, for example, when objects are added to a 'Set' or 'Map', where these methods will be called often. The inspection will not report when the objects are created in a 'throw' or 'assert' statement. Example: 'class Person {\n private String name;\n private int age;\n\n public boolean equals(Object o) {\n return Arrays.equals(new Object[] {name, age}, new Object[] {((Foo)o).name, ((Foo)o).age});\n }\n\n public int hashCode() {\n return (name + age).hashCode();\n }\n }' In this example, two additional arrays are created inside 'equals()', usages of 'age' field require boxing, and 'name + age' implicitly creates a new string.", + "markdown": "Reports construction of (temporary) new objects inside `equals()`, `hashCode()`, `compareTo()`, and `Comparator.compare()` methods.\n\n\nBesides constructor invocations, new objects can also be created by autoboxing or iterator creation inside a\n`foreach` statement.\nThis can cause performance problems, for example, when objects are added to a `Set` or `Map`,\nwhere these methods will be called often.\n\n\nThe inspection will not report when the objects are created in a `throw` or `assert` statement.\n\n**Example:**\n\n\n class Person {\n private String name;\n private int age;\n\n public boolean equals(Object o) {\n return Arrays.equals(new Object[] {name, age}, new Object[] {((Foo)o).name, ((Foo)o).age});\n }\n\n public int hashCode() {\n return (name + age).hashCode();\n }\n }\n\n\nIn this example, two additional arrays are created inside `equals()`, usages of `age` field require boxing,\nand `name + age` implicitly creates a new string." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ObjectInstantiationInEqualsHashCode", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UtilityClassWithPublicConstructor", + "shortDescription": { + "text": "Utility class with 'public' constructor" + }, + "fullDescription": { + "text": "Reports utility classes with 'public' constructors. Utility classes have all fields and methods declared as 'static'. Creating a 'public' constructor in such classes is confusing and may cause accidental class instantiation. Example: 'public final class UtilityClass {\n public UtilityClass(){\n }\n public static void foo() {}\n }' After the quick-fix is applied: 'public final class UtilityClass {\n private UtilityClass(){\n }\n public static void foo() {}\n }'", + "markdown": "Reports utility classes with `public` constructors.\n\nUtility classes have all fields and methods declared as `static`. Creating a `public`\nconstructor in such classes is confusing and may cause accidental class instantiation.\n\n**Example:**\n\n\n public final class UtilityClass {\n public UtilityClass(){\n }\n public static void foo() {}\n }\n\nAfter the quick-fix is applied:\n\n\n public final class UtilityClass {\n private UtilityClass(){\n }\n public static void foo() {}\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UtilityClassWithPublicConstructor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TypeParameterExtendsFinalClass", + "shortDescription": { + "text": "Type parameter extends 'final' class" + }, + "fullDescription": { + "text": "Reports type parameters declared to extend a 'final' class. Suggests replacing the type parameter with the type of the specified 'final' class since 'final' classes cannot be extended. Example: 'void foo() {\n List list; // Warning: the Integer class is a final class\n }' After the quick-fix is applied: 'void foo() {\n List list;\n }'", + "markdown": "Reports type parameters declared to extend a `final` class.\n\nSuggests replacing the type parameter with the type of the specified `final` class since\n`final` classes cannot be extended.\n\n**Example:**\n\n\n void foo() {\n List list; // Warning: the Integer class is a final class\n }\n\nAfter the quick-fix is applied:\n\n\n void foo() {\n List list;\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "TypeParameterExtendsFinalClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Inheritance issues", + "index": 25, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TrivialIf", + "shortDescription": { + "text": "Redundant 'if' statement" + }, + "fullDescription": { + "text": "Reports 'if' statements that can be simplified to a single assignment, 'return', or 'assert' statement. Example: 'if (foo()) {\n return true;\n } else {\n return false;\n }' After the quick-fix is applied: 'return foo();' Configure the inspection: Use the Ignore chained 'if' statements option if you want to hide a warning for chained 'if' statements. For example, in the following code the warning will be hidden, but the quick-fix will still be available: 'if (condition1) return true;\n if (condition2) return false;\n return true;' Note that replacing 'if (isTrue()) assert false;' with 'assert isTrue();' may change the program semantics when asserts are disabled if condition has side effects. Use the Ignore 'if' statements with trivial 'assert' option if you want to hide a warning for 'if' statements containing only 'assert' statement in their bodies.", + "markdown": "Reports `if` statements that can be simplified to a single assignment, `return`, or `assert` statement.\n\nExample:\n\n\n if (foo()) {\n return true;\n } else {\n return false;\n }\n\nAfter the quick-fix is applied:\n\n\n return foo();\n\nConfigure the inspection:\n\nUse the **Ignore chained 'if' statements** option if you want to hide a warning for chained `if` statements.\n\nFor example, in the following code the warning will be hidden, but the quick-fix will still be available:\n\n\n if (condition1) return true;\n if (condition2) return false;\n return true;\n\nNote that replacing `if (isTrue()) assert false;` with `assert isTrue();` may change the program semantics\nwhen asserts are disabled if condition has side effects.\nUse the **Ignore 'if' statements with trivial 'assert'** option if you want to hide a warning for `if` statements\ncontaining only `assert` statement in their bodies." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantIfStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnconditionalWait", + "shortDescription": { + "text": "Unconditional 'wait()' call" + }, + "fullDescription": { + "text": "Reports 'wait()' being called unconditionally within a synchronized context. Normally, 'wait()' is used to block a thread until some condition is true. If 'wait()' is called unconditionally, it often indicates that the condition was checked before a lock was acquired. In that case a data race may occur, with the condition becoming true between the time it was checked and the time the lock was acquired. While constructs found by this inspection are not necessarily incorrect, they are certainly worth examining. Example: 'class Bar {\n void foo() throws InterruptedException {\n synchronized (this) {\n wait(); // warning\n }\n }\n }'", + "markdown": "Reports `wait()` being called unconditionally within a synchronized context.\n\n\nNormally, `wait()` is used to block a thread until some condition is true. If\n`wait()` is called unconditionally, it often indicates that the condition was\nchecked before a lock was acquired. In that case a data race may occur, with the condition\nbecoming true between the time it was checked and the time the lock was acquired.\n\n\nWhile constructs found by this inspection are not necessarily incorrect, they are certainly worth examining.\n\n**Example:**\n\n\n class Bar {\n void foo() throws InterruptedException {\n synchronized (this) {\n wait(); // warning\n }\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnconditionalWait", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BooleanMethodIsAlwaysInverted", + "shortDescription": { + "text": "Boolean method is always inverted" + }, + "fullDescription": { + "text": "Reports methods with a 'boolean' return type that are always negated when called. A quick-fix is provided to invert and optionally rename the method. For performance reasons, not all problematic methods may be highlighted in the editor. Example: 'class C {\n boolean alwaysTrue() {\n return true;\n }\n\n void f() {\n if (!alwaysTrue()) {\n return;\n }\n }\n boolean member = !alwaysTrue();\n }' After the quick-fix is applied: 'class C {\n boolean alwaysFalse() {\n return false;\n }\n\n void f() {\n if (alwaysFalse()) {\n return;\n }\n }\n boolean member = alwaysFalse();\n }'", + "markdown": "Reports methods with a `boolean` return type that are always negated when called.\n\nA quick-fix is provided to invert and optionally rename the method.\nFor performance reasons, not all problematic methods may be highlighted in the editor.\n\nExample:\n\n\n class C {\n boolean alwaysTrue() {\n return true;\n }\n\n void f() {\n if (!alwaysTrue()) {\n return;\n }\n }\n boolean member = !alwaysTrue();\n }\n\nAfter the quick-fix is applied:\n\n\n class C {\n boolean alwaysFalse() {\n return false;\n }\n\n void f() {\n if (alwaysFalse()) {\n return;\n }\n }\n boolean member = alwaysFalse();\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "BooleanMethodIsAlwaysInverted", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Data flow", + "index": 23, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InstanceGuardedByStatic", + "shortDescription": { + "text": "Instance member guarded by static field" + }, + "fullDescription": { + "text": "Reports '@GuardedBy' annotations on instance fields or methods in which the guard is a 'static' field. Guarding a non-static by a static may result in excessive lock contention, as access to each locked field in any object instance will prevent simultaneous access to that field in every object instance. Example: 'private static ReadWriteLock lock = new ReentrantReadWriteLock(); //static guarding field\n private Object state;\n\n @GuardedBy(\"lock\")\n public void bar() {\n state = new Object();\n }' Supported '@GuardedBy' annotations are: 'net.jcip.annotations.GuardedBy' 'javax.annotation.concurrent.GuardedBy' 'org.apache.http.annotation.GuardedBy' 'com.android.annotations.concurrency.GuardedBy' 'androidx.annotation.GuardedBy' 'com.google.errorprone.annotations.concurrent.GuardedBy'", + "markdown": "Reports `@GuardedBy` annotations on instance fields or methods in which the guard is a `static` field. Guarding a non-static by a static may result in excessive lock contention, as access to each locked field in any object instance will prevent simultaneous access to that field in every object instance.\n\nExample:\n\n\n private static ReadWriteLock lock = new ReentrantReadWriteLock(); //static guarding field\n private Object state;\n\n @GuardedBy(\"lock\")\n public void bar() {\n state = new Object();\n }\n\nSupported `@GuardedBy` annotations are:\n\n* `net.jcip.annotations.GuardedBy`\n* `javax.annotation.concurrent.GuardedBy`\n* `org.apache.http.annotation.GuardedBy`\n* `com.android.annotations.concurrency.GuardedBy`\n* `androidx.annotation.GuardedBy`\n* `com.google.errorprone.annotations.concurrent.GuardedBy`" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "InstanceGuardedByStatic", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Concurrency annotation issues", + "index": 61, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AutoCloseableResource", + "shortDescription": { + "text": "AutoCloseable used without 'try'-with-resources" + }, + "fullDescription": { + "text": "Reports 'AutoCloseable' instances which are not used in a try-with-resources statement, also known as Automatic Resource Management. This means that the \"open resource before/in 'try', close in 'finally'\" style that had been used before try-with-resources became available, is also reported. This inspection is meant to replace all opened but not safely closed inspections when developing in Java 7 and higher. Example: 'private static void foo() throws IOException {\n InputStream profile = Thread.currentThread().getContextClassLoader().getResourceAsStream(\"/someFile\");\n System.out.println(profile.read());\n }' Use the following options to configure the inspection: List subclasses of 'AutoCloseable' that do not need to be closed and should be ignored by this inspection. Note: The inspection will still report streams returned from the 'java.nio.file.Files' methods 'lines()', 'walk()', 'list()' and 'find()', even when 'java.util.stream.Stream' is listed to be ignored. These streams contain an associated I/O resource that needs to be closed. List methods returning 'AutoCloseable' that should be ignored when called. Whether to ignore an 'AutoCloseable' if it is the result of a method call. When this option is enabled, the results of factory methods will also be ignored. Whether the inspection should report if an 'AutoCloseable' instance is passed as a method call argument. If this option is enabled, the inspection assumes the resource is closed in the called method. Method calls inside a 'finally' block with 'close' in the name and an 'AutoCloseable' argument will not be ignored. Whether to ignore method references to constructors of resource classes. Whether to ignore methods that return a resource and whose name starts with 'get'. This can reduce false positives because most of the getters do not transfer the ownership of the resource, and their call sites are not responsible for closing the resource.", + "markdown": "Reports `AutoCloseable` instances which are not used in a try-with-resources statement, also known as *Automatic Resource Management* .\n\n\nThis means that the \"open resource before/in `try`, close in `finally`\" style that had been used before\ntry-with-resources became available, is also reported.\nThis inspection is meant to replace all *opened but not safely closed* inspections when developing in Java 7 and higher.\n\n**Example:**\n\n\n private static void foo() throws IOException {\n InputStream profile = Thread.currentThread().getContextClassLoader().getResourceAsStream(\"/someFile\");\n System.out.println(profile.read());\n }\n\n\nUse the following options to configure the inspection:\n\n* List subclasses of `AutoCloseable` that do not need to be closed and should be ignored by this inspection. \n **Note** : The inspection will still report streams returned from the `java.nio.file.Files` methods `lines()`, `walk()`, `list()` and `find()`, even when `java.util.stream.Stream` is listed to be ignored. These streams contain an associated I/O resource that needs to be closed.\n* List methods returning `AutoCloseable` that should be ignored when called.\n* Whether to ignore an `AutoCloseable` if it is the result of a method call. When this option is enabled, the results of factory methods will also be ignored.\n* Whether the inspection should report if an `AutoCloseable` instance is passed as a method call argument. If this option is enabled, the inspection assumes the resource is closed in the called method. Method calls inside a `finally` block with 'close' in the name and an `AutoCloseable` argument will not be ignored.\n* Whether to ignore method references to constructors of resource classes.\n* Whether to ignore methods that return a resource and whose name starts with 'get'. This can reduce false positives because most of the getters do not transfer the ownership of the resource, and their call sites are not responsible for closing the resource." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "resource", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Resource management", + "index": 47, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MethodOverridesInaccessibleMethodOfSuper", + "shortDescription": { + "text": "Method overrides inaccessible method of superclass" + }, + "fullDescription": { + "text": "Reports methods with the same signature as an inaccessible method of a superclass, for example, a private method, or a package-private method of a superclass in another package. Such method names may be confusing because the method in the subclass may look like an override when in fact it hides the inaccessible method of the superclass. Moreover, if the visibility of the method in the superclass changes later, it may either silently change the semantics of the subclass or cause a compilation error. A quick-fix is suggested to rename the method. Example: 'public class Super {\n private void test() {\n }\n }\n\n public class Sub extends Super {\n void test() { // making 'Super.test()' public causes a compilation error\n // making 'Super.test()' package-private makes 'Sub.test()' an override\n }\n }'", + "markdown": "Reports methods with the same signature as an inaccessible method of a superclass, for example, a private method, or a package-private method of a superclass in another package.\n\n\nSuch method names may be confusing because the method in the subclass may look like an override when in fact\nit hides the inaccessible method of the superclass.\nMoreover, if the visibility of the method in the superclass changes later,\nit may either silently change the semantics of the subclass or cause a compilation error.\n\nA quick-fix is suggested to rename the method.\n\n**Example:**\n\n\n public class Super {\n private void test() {\n }\n }\n\n public class Sub extends Super {\n void test() { // making 'Super.test()' public causes a compilation error\n // making 'Super.test()' package-private makes 'Sub.test()' an override\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MethodOverridesInaccessibleMethodOfSuper", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Visibility", + "index": 57, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IntLiteralMayBeLongLiteral", + "shortDescription": { + "text": "Cast to 'long' can be 'long' literal" + }, + "fullDescription": { + "text": "Reports 'int' literal expressions that are immediately cast to 'long'. Such literal expressions can be replaced with equivalent 'long' literals. Example: 'Long l = (long)42;' After the quick-fix is applied: 'Long l = 42L;'", + "markdown": "Reports `int` literal expressions that are immediately cast to `long`.\n\nSuch literal expressions can be replaced with equivalent `long` literals.\n\n**Example:**\n\n Long l = (long)42;\n\nAfter the quick-fix is applied:\n\n Long l = 42L;\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "IntLiteralMayBeLongLiteral", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues/Cast", + "index": 102, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SingleStatementInBlock", + "shortDescription": { + "text": "Code block contains single statement" + }, + "fullDescription": { + "text": "Reports control flow statements with a single statement in their code block and suggests removing the braces from the control flow statement body. Example: 'if (x > 0) {\n System.out.println(\"x is positive\");\n }' After the quick-fix is applied: 'if (x > 0) System.out.println(\"x is positive\");'", + "markdown": "Reports control flow statements with a single statement in their code block and suggests removing the braces from the control flow statement body.\n\nExample:\n\n\n if (x > 0) {\n System.out.println(\"x is positive\");\n }\n\nAfter the quick-fix is applied:\n\n\n if (x > 0) System.out.println(\"x is positive\");\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "SingleStatementInBlock", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TestCaseWithConstructor", + "shortDescription": { + "text": "TestCase with non-trivial constructors" + }, + "fullDescription": { + "text": "Reports test cases with initialization logic in their constructors. If a constructor fails, the '@After' annotated or 'tearDown()' method won't be called. This can leave the test environment partially initialized, which can adversely affect other tests. Instead, initialization of test cases should be done in a 'setUp()' or '@Before' annotated method. Bad example: 'public class ImportantTest {\n private File file;\n\n public ImportantTest() throws IOException {\n file = File.createTempFile(\"xyz\", \".tmp\");\n }\n\n // ... tests go here\n }'", + "markdown": "Reports test cases with initialization logic in their constructors. If a constructor fails, the `@After` annotated or `tearDown()` method won't be called. This can leave the test environment partially initialized, which can adversely affect other tests. Instead, initialization of test cases should be done in a `setUp()` or `@Before` annotated method.\n\nBad example:\n\n\n public class ImportantTest {\n private File file;\n\n public ImportantTest() throws IOException {\n file = File.createTempFile(\"xyz\", \".tmp\");\n }\n\n // ... tests go here\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "JUnitTestCaseWithNonTrivialConstructors", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages/Test frameworks", + "index": 94, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SingleCharacterStartsWith", + "shortDescription": { + "text": "Single character 'startsWith()' or 'endsWith()'" + }, + "fullDescription": { + "text": "Reports calls to 'String.startsWith()' and 'String.endsWith()' where single character string literals are passed as an argument. A quick-fix is suggested to replace such calls with more efficiently implemented 'String.charAt()'. However, the performance gain of such change is minimal and the code becomes less readable because of the extra non-zero length check, so it is recommended to apply the quick-fix only inside tight loops. This inspection is intended for Java ME and other highly resource constrained environments. Applying the results of this inspection without consideration might have negative effects on code clarity and design. Example: 'boolean startsWithX(String s) {\n return s.startsWith(\"x\");\n }' After the quick-fix is applied: 'boolean startsWithX(String s) {\n return !s.isEmpty() && s.charAt(0) == 'x';\n }'", + "markdown": "Reports calls to `String.startsWith()` and `String.endsWith()` where single character string literals are passed as an argument.\n\n\nA quick-fix is suggested to replace such calls with more efficiently implemented `String.charAt()`.\n\n\nHowever, the performance gain of such change is minimal and the code becomes less readable because of the extra non-zero length check,\nso it is recommended to apply the quick-fix only inside tight loops.\n\n\nThis inspection is intended for Java ME and other highly resource constrained environments.\nApplying the results of this inspection without consideration might have negative effects on code clarity and design.\n\n**Example:**\n\n\n boolean startsWithX(String s) {\n return s.startsWith(\"x\");\n }\n\nAfter the quick-fix is applied:\n\n\n boolean startsWithX(String s) {\n return !s.isEmpty() && s.charAt(0) == 'x';\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SingleCharacterStartsWith", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance/Embedded", + "index": 19, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UtilityClassCanBeEnum", + "shortDescription": { + "text": "Utility class can be 'enum'" + }, + "fullDescription": { + "text": "Reports utility classes that can be converted to enums. Some coding style guidelines require implementing utility classes as enums to avoid code coverage issues in 'private' constructors. Example: 'class StringUtils {\n public static final String EMPTY = \"\";\n }' After the quick-fix is applied: 'enum StringUtils {\n ;\n public static final String EMPTY = \"\";\n }'", + "markdown": "Reports utility classes that can be converted to enums.\n\nSome coding style guidelines require implementing utility classes as enums\nto avoid code coverage issues in `private` constructors.\n\n**Example:**\n\n\n class StringUtils {\n public static final String EMPTY = \"\";\n }\n\nAfter the quick-fix is applied:\n\n\n enum StringUtils {\n ;\n public static final String EMPTY = \"\";\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UtilityClassCanBeEnum", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReadObjectAndWriteObjectPrivate", + "shortDescription": { + "text": "'readObject()' or 'writeObject()' not declared 'private'" + }, + "fullDescription": { + "text": "Reports 'Serializable' classes where the 'readObject' or 'writeObject' methods are not declared private. There is no reason these methods should ever have a higher visibility than 'private'. A quick-fix is suggested to make the corresponding method 'private'. Example: 'public class Test implements Serializable {\n public void readObject(ObjectInputStream stream) {\n /* ... */\n }\n }' After the quick-fix is applied: 'public class Test implements Serializable {\n private void readObject(ObjectInputStream stream) {\n /* ... */\n }\n }'", + "markdown": "Reports `Serializable` classes where the `readObject` or `writeObject` methods are not declared private. There is no reason these methods should ever have a higher visibility than `private`.\n\n\nA quick-fix is suggested to make the corresponding method `private`.\n\n**Example:**\n\n\n public class Test implements Serializable {\n public void readObject(ObjectInputStream stream) {\n /* ... */\n }\n }\n\nAfter the quick-fix is applied:\n\n\n public class Test implements Serializable {\n private void readObject(ObjectInputStream stream) {\n /* ... */\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonPrivateSerializationMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ExtendsObject", + "shortDescription": { + "text": "Class explicitly extends 'Object'" + }, + "fullDescription": { + "text": "Reports any classes that are explicitly declared to extend 'java.lang.Object'. Such declaration is redundant and can be safely removed. Example: 'class MyClass extends Object {\n }' The quick-fix removes the redundant 'extends Object' clause: 'class MyClass {\n }'", + "markdown": "Reports any classes that are explicitly declared to extend `java.lang.Object`.\n\nSuch declaration is redundant and can be safely removed.\n\nExample:\n\n\n class MyClass extends Object {\n }\n\nThe quick-fix removes the redundant `extends Object` clause:\n\n\n class MyClass {\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassExplicitlyExtendsObject", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantFileCreation", + "shortDescription": { + "text": "Redundant 'File' instance creation" + }, + "fullDescription": { + "text": "Reports redundant 'File' creation in one of the following constructors when only 'String' path can be used: 'FileInputStream', 'FileOutputStream', 'FileReader', 'FileWriter', 'PrintStream', 'PrintWriter', 'Formatter'. Example: 'InputStream is = new FileInputStream(new File(\"in.txt\"));' After quick-fix is applied: 'InputStream is = new FileInputStream(\"in.txt\");' New in 2020.3", + "markdown": "Reports redundant `File` creation in one of the following constructors when only `String` path can be used: `FileInputStream`, `FileOutputStream`, `FileReader`, `FileWriter`, `PrintStream`, `PrintWriter`, `Formatter`.\n\nExample:\n\n\n InputStream is = new FileInputStream(new File(\"in.txt\"));\n\nAfter quick-fix is applied:\n\n\n InputStream is = new FileInputStream(\"in.txt\");\n\nNew in 2020.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantFileCreation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PointlessBooleanExpression", + "shortDescription": { + "text": "Pointless boolean expression" + }, + "fullDescription": { + "text": "Reports unnecessary or overly complicated boolean expressions. Such expressions include '&&'-ing with 'true', '||'-ing with 'false', equality comparison with a boolean literal, or negation of a boolean literal. Such expressions can be simplified. Example: 'boolean a = !(x && false);\n boolean b = false || x;\n boolean c = x != true;' After the quick-fix is applied: 'boolean a = true;\n boolean b = x;\n boolean c = !x;' Configure the inspection: Use the Ignore named constants in determining pointless expressions option to ignore named constants when determining if an expression is pointless.", + "markdown": "Reports unnecessary or overly complicated boolean expressions.\n\nSuch expressions include `&&`-ing with `true`,\n`||`-ing with `false`,\nequality comparison with a boolean literal, or negation of a boolean literal. Such expressions can be simplified.\n\nExample:\n\n\n boolean a = !(x && false);\n boolean b = false || x;\n boolean c = x != true;\n\nAfter the quick-fix is applied:\n\n\n boolean a = true;\n boolean b = x;\n boolean c = !x;\n\n\nConfigure the inspection:\nUse the **Ignore named constants in determining pointless expressions** option to ignore named constants when determining if an expression is pointless." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "PointlessBooleanExpression", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuperTearDownInFinally", + "shortDescription": { + "text": "JUnit 3 'super.tearDown()' is not called from 'finally' block" + }, + "fullDescription": { + "text": "Reports calls of the JUnit 3's 'super.tearDown()' method that are not performed inside a 'finally' block. If an exception is thrown before 'super.tearDown()' is called it could lead to inconsistencies and leaks. Example: 'public class AnotherTest extends CompanyTestCase {\n private Path path;\n\n @Override\n protected void setUp() throws Exception {\n super.setUp();\n path = Files.createTempFile(\"File\", \".tmp\");\n }\n\n @Override\n protected void tearDown() throws Exception {\n Files.delete(path);\n super.tearDown();\n }\n }' Improved code: 'public class AnotherTest extends CompanyTestCase {\n private Path path;\n\n @Override\n protected void setUp() throws Exception {\n super.setUp();\n path = Files.createTempFile(\"File\", \".tmp\");\n }\n\n @Override\n protected void tearDown() throws Exception {\n try {\n Files.delete(path);\n } finally {\n super.tearDown();\n }\n }\n }'", + "markdown": "Reports calls of the JUnit 3's `super.tearDown()` method that are not performed inside a `finally` block. If an exception is thrown before `super.tearDown()` is called it could lead to inconsistencies and leaks.\n\n**Example:**\n\n\n public class AnotherTest extends CompanyTestCase {\n private Path path;\n\n @Override\n protected void setUp() throws Exception {\n super.setUp();\n path = Files.createTempFile(\"File\", \".tmp\");\n }\n\n @Override\n protected void tearDown() throws Exception {\n Files.delete(path);\n super.tearDown();\n }\n }\n\nImproved code:\n\n\n public class AnotherTest extends CompanyTestCase {\n private Path path;\n\n @Override\n protected void setUp() throws Exception {\n super.setUp();\n path = Files.createTempFile(\"File\", \".tmp\");\n }\n\n @Override\n protected void tearDown() throws Exception {\n try {\n Files.delete(path);\n } finally {\n super.tearDown();\n }\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SuperTearDownInFinally", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages/Test frameworks", + "index": 94, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReassignedVariable", + "shortDescription": { + "text": "Reassigned variable" + }, + "fullDescription": { + "text": "Reports reassigned variables, which complicate reading and understanding the code. Example: 'int value = 2 * (height + width);\n System.out.println(\"perimeter: \" + value);\n\n value = height * width;\n System.out.println(\"area: \" + value);'", + "markdown": "Reports reassigned variables, which complicate reading and understanding the code.\n\nExample:\n\n\n int value = 2 * (height + width);\n System.out.println(\"perimeter: \" + value);\n\n value = height * width;\n System.out.println(\"area: \" + value);\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReassignedVariable", + "ideaSeverity": "TEXT ATTRIBUTES", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ListenerMayUseAdapter", + "shortDescription": { + "text": "Class may extend adapter instead of implementing listener" + }, + "fullDescription": { + "text": "Reports classes implementing listeners instead of extending corresponding adapters. A quick-fix is available to remove any redundant empty methods left after replacing a listener implementation with an adapter extension. Use the Only warn when empty implementing methods are found option to configure the inspection to warn even if no empty methods are found.", + "markdown": "Reports classes implementing listeners instead of extending corresponding adapters.\n\nA quick-fix is available to\nremove any redundant empty methods left after replacing a listener implementation with an adapter extension.\n\n\nUse the **Only warn when empty implementing methods are found** option to configure the inspection to warn even if no empty methods are found." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ListenerMayUseAdapter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MethodNameSameAsClassName", + "shortDescription": { + "text": "Method name same as class name" + }, + "fullDescription": { + "text": "Reports methods that are named identically to their class. While such naming is allowed by the Java language, by convention it is reserved for defining constructors. Using it for methods is probably a mistake or bad practice. Example: 'class MyClass {\n int val;\n\n // Method MyClass named identically to its containing class.\n // Likely, 'void' was added by mistake.\n void MyClass(int val) {\n this.val = val;\n }\n }' When appropriate, a quick-fix converts the method to a constructor: 'class MyClass {\n int val;\n\n MyClass(int val) {\n this.val = val;\n }\n }' Another quick-fix renames the method.", + "markdown": "Reports methods that are named identically to their class. While such naming is allowed by the Java language, by convention it is reserved for defining constructors. Using it for methods is probably a mistake or bad practice.\n\n**Example:**\n\n\n class MyClass {\n int val;\n\n // Method MyClass named identically to its containing class.\n // Likely, 'void' was added by mistake.\n void MyClass(int val) {\n this.val = val;\n }\n }\n\nWhen appropriate, a quick-fix converts the method to a constructor:\n\n\n class MyClass {\n int val;\n\n MyClass(int val) {\n this.val = val;\n }\n }\n\nAnother quick-fix renames the method." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MethodNameSameAsClassName", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions/Method", + "index": 88, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LocalVariableNamingConvention", + "shortDescription": { + "text": "Local variable naming convention" + }, + "fullDescription": { + "text": "Reports local variables whose names are too short, too long, or do not follow the specified regular expression pattern. Example: 'int X = 42;' should be reported if the inspection is enabled with the default settings in which a variable name should start with a lowercase letter. Configure the inspection: Use the fields in the Options section to specify the minimum length, maximum length, and a regular expression expected for local variable names. Specify 0 in order not to check the length of names. Regular expressions should be specified in the standard java.util.regex format. Use checkboxes to ignore 'for'-loop and 'catch' section parameters.", + "markdown": "Reports local variables whose names are too short, too long, or do not follow the specified regular expression pattern.\n\n**Example:** `int X = 42;`\nshould be reported if the inspection is enabled with the default settings in which a variable name should start with a lowercase letter.\n\nConfigure the inspection:\n\n\nUse the fields in the **Options** section to specify the minimum length, maximum length, and a regular expression expected for local variable names.\nSpecify **0** in order not to check the length of names. Regular expressions should be specified in the standard **java.util.regex** format.\n\nUse checkboxes to ignore `for`-loop and `catch` section parameters." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LocalVariableNamingConvention", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions", + "index": 51, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryJavaDocLink", + "shortDescription": { + "text": "Unnecessary Javadoc link" + }, + "fullDescription": { + "text": "Reports Javadoc '@see', '{@link}', and '{@linkplain}' tags that refer to the method owning the comment, the super method of the method owning the comment, or the class containing the comment. Such links are unnecessary and can be safely removed with this inspection's quick-fix. The quick-fix will remove the entire Javadoc comment if the tag is its only content. Example: 'class Example {\n /**\n * @see Example#method\n */\n public void method() { }\n }' After the quick-fix is applied: 'class Example {\n public void method() { }\n}' Use the checkbox below to ignore inline links ('{@link}' and '{@linkplain}') to super methods. Although a link to all super methods is automatically added by the Javadoc tool, an inline link to the super method may sometimes be needed in texts of the Javadoc comments.", + "markdown": "Reports Javadoc `@see`, `{@link}`, and `{@linkplain}` tags that refer to the method owning the comment, the super method of the method owning the comment, or the class containing the comment.\n\nSuch links are unnecessary and can be safely removed with this inspection's quick-fix. The\nquick-fix will remove the entire Javadoc comment if the tag is its only content.\n\n**Example:**\n\n\n class Example {\n /**\n * @see Example#method\n */\n public void method() { }\n }\n\nAfter the quick-fix is applied:\n\n\n class Example {\n public void method() { }\n }\n\n\nUse the checkbox below to ignore inline links (`{@link}` and `{@linkplain}`)\nto super methods. Although a link to all super methods is automatically added by the\nJavadoc tool, an inline link to the super method may sometimes be needed in texts of the Javadoc comments." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryJavaDocLink", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Javadoc", + "index": 45, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NestedSwitchStatement", + "shortDescription": { + "text": "Nested 'switch' statement" + }, + "fullDescription": { + "text": "Reports nested 'switch' statements or expressions. Nested 'switch' statements may result in extremely confusing code. These statements may be extracted to a separate method. Example: 'int res = switch (i) {\n case 0 -> 0;\n default -> switch (i) {\n case 100 -> 0;\n default -> i;\n };\n };'", + "markdown": "Reports nested `switch` statements or expressions.\n\nNested `switch` statements\nmay result in extremely confusing code. These statements may be extracted to a separate method.\n\nExample:\n\n\n int res = switch (i) {\n case 0 -> 0;\n default -> switch (i) {\n case 100 -> 0;\n default -> i;\n };\n };\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NestedSwitchStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EmptyTryBlock", + "shortDescription": { + "text": "Empty 'try' block" + }, + "fullDescription": { + "text": "Reports empty 'try' blocks, including try-with-resources statements. 'try' blocks with comments are considered empty. This inspection doesn't report empty 'try' blocks found in JSP files.", + "markdown": "Reports empty `try` blocks, including try-with-resources statements.\n\n`try` blocks with comments are considered empty.\n\n\nThis inspection doesn't report empty `try` blocks found in JSP files." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "EmptyTryBlock", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CollectionsFieldAccessReplaceableByMethodCall", + "shortDescription": { + "text": "Reference to empty collection field can be replaced with method call" + }, + "fullDescription": { + "text": "Reports usages of 'java.util.Collections' fields: 'EMPTY_LIST', 'EMPTY_MAP' or 'EMPTY_SET'. These field usages may be replaced with the following method calls: 'emptyList()', 'emptyMap()', or 'emptySet()'. Such method calls prevent unchecked warnings by the compiler because the type parameters can be inferred. Example: 'List emptyList = Collections.EMPTY_LIST;' After the quick-fix is applied: 'List emptyList = Collections.emptyList();' This inspection only reports if the language level of the project or module is 5 or higher.", + "markdown": "Reports usages of `java.util.Collections` fields: `EMPTY_LIST`, `EMPTY_MAP` or `EMPTY_SET`. These field usages may be replaced with the following method calls: `emptyList()`, `emptyMap()`, or `emptySet()`. Such method calls prevent unchecked warnings by the compiler because the type parameters can be inferred.\n\n**Example:**\n\n\n List emptyList = Collections.EMPTY_LIST;\n\nAfter the quick-fix is applied:\n\n\n List emptyList = Collections.emptyList();\n\nThis inspection only reports if the language level of the project or module is 5 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CollectionsFieldAccessReplaceableByMethodCall", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 5", + "index": 53, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AssertWithSideEffects", + "shortDescription": { + "text": "'assert' statement with side effects" + }, + "fullDescription": { + "text": "Reports 'assert' statements that cause side effects. Since assertions can be switched off, these side effects are not guaranteed, which can cause subtle bugs. Common unwanted side effects detected by this inspection are modifications of variables and fields. When methods calls are involved, they are analyzed one level deep. Example: 'assert i++ < 10;'", + "markdown": "Reports `assert` statements that cause side effects.\n\n\nSince assertions can be switched off,\nthese side effects are not guaranteed, which can cause subtle bugs. Common unwanted side effects detected by this inspection are\nmodifications of variables and fields. When methods calls are involved, they are analyzed one level deep.\n\n**Example:**\n\n\n assert i++ < 10;\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "AssertWithSideEffects", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "WaitOrAwaitWithoutTimeout", + "shortDescription": { + "text": "'wait()' or 'await()' without timeout" + }, + "fullDescription": { + "text": "Reports calls to 'Object.wait()' or 'Condition.await()' without specifying a timeout. Such calls may be dangerous in high-availability programs, as failures in one component may result in blockages of the waiting component if 'notify()'/'notifyAll()' or 'signal()'/'signalAll()' never get called. Example: 'void foo(Object bar) throws InterruptedException {\n bar.wait();\n }'", + "markdown": "Reports calls to `Object.wait()` or `Condition.await()` without specifying a timeout.\n\n\nSuch calls may be dangerous in high-availability programs, as failures in one\ncomponent may result in blockages of the waiting component\nif `notify()`/`notifyAll()`\nor `signal()`/`signalAll()` never get called.\n\n**Example:**\n\n\n void foo(Object bar) throws InterruptedException {\n bar.wait();\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "WaitOrAwaitWithoutTimeout", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RefusedBequest", + "shortDescription": { + "text": "Method does not call super method" + }, + "fullDescription": { + "text": "Reports methods that override a super method without calling it. This is also known as a refused bequest. Such methods may represent a failure of abstraction and cause hard-to-trace bugs. The inspection doesn't report methods overridden from 'java.lang.Object', except for 'clone()'. The 'clone()' method should by convention call its super method, which will return an object of the correct type. Example 1: 'class A {\n @Override\n public Object clone() {\n // does not call 'super.clone()'\n return new A();\n }\n }' Example 2: 'interface I {\n default void foo() {}\n }\n\n class A implements I {\n // warning on method when\n // 'Ignore 'default' super methods' is disabled\n @Override\n public void foo(){}\n }' Configure the inspection: Use the Only report when super method is annotated by option to ignore super methods marked with the annotations from the provided list. You can manually add annotations to the list. Use the Ignore empty super methods option to ignore super methods that are either empty or only throw an exception. Use the Ignore 'default' super methods option to ignore 'default' super methods from interfaces.", + "markdown": "Reports methods that override a super method without calling it. This is also known as a *refused bequest* . Such methods may represent a failure of abstraction and cause hard-to-trace bugs.\n\n\nThe inspection doesn't report methods overridden from `java.lang.Object`, except for `clone()`.\nThe `clone()` method should by convention call its super method,\nwhich will return an object of the correct type.\n\n**Example 1:**\n\n\n class A {\n @Override\n public Object clone() {\n // does not call 'super.clone()'\n return new A();\n }\n }\n\n**Example 2:**\n\n\n interface I {\n default void foo() {}\n }\n\n class A implements I {\n // warning on method when\n // 'Ignore 'default' super methods' is disabled\n @Override\n public void foo(){}\n }\n\nConfigure the inspection:\n\n* Use the **Only report when super method is annotated by** option to ignore super methods marked with the annotations from the provided list. You can manually add annotations to the list.\n* Use the **Ignore empty super methods** option to ignore super methods that are either empty or only throw an exception.\n* Use the **Ignore 'default' super methods** option to ignore `default` super methods from interfaces." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "MethodDoesntCallSuperMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Inheritance issues", + "index": 25, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FinalClass", + "shortDescription": { + "text": "Class is closed to inheritance" + }, + "fullDescription": { + "text": "Reports classes that are declared 'final'. Final classes that extend a 'sealed' class or interface are not reported. Such classes can't be inherited and may indicate a lack of object-oriented design. Some coding standards discourage 'final' classes. Example: 'public final class Main {\n }' After the quick-fix is applied: 'public class Main {\n }'", + "markdown": "Reports classes that are declared `final`. Final classes that extend a `sealed` class or interface are not reported. Such classes can't be inherited and may indicate a lack of object-oriented design. Some coding standards discourage `final` classes.\n\n**Example:**\n\n\n public final class Main {\n }\n\nAfter the quick-fix is applied:\n\n\n public class Main {\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "FinalClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryEmptyArrayUsage", + "shortDescription": { + "text": "Unnecessary zero length array usage" + }, + "fullDescription": { + "text": "Reports allocations of arrays with known lengths of zero when there is a constant for that in the class of the array's element type. As zero-length arrays are immutable, you can save memory reusing the same array instance. Example: 'class Item {\n // Public zero-length array constant that can be reused \n public static final Item[] EMPTY_ARRAY = new Item[0];\n }\n class EmptyNode {\n Item[] getChildren() {\n // Unnecessary zero-length array creation\n return new Item[0];\n }\n }' After the quick-fix is applied: 'class EmptyNode {\n Item[] getChildren() {\n return Item.EMPTY_ARRAY;\n }\n }'", + "markdown": "Reports allocations of arrays with known lengths of zero when there is a constant for that in the class of the array's element type. As zero-length arrays are immutable, you can save memory reusing the same array instance.\n\n**Example:**\n\n\n class Item {\n // Public zero-length array constant that can be reused \n public static final Item[] EMPTY_ARRAY = new Item[0];\n }\n class EmptyNode {\n Item[] getChildren() {\n // Unnecessary zero-length array creation\n return new Item[0];\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class EmptyNode {\n Item[] getChildren() {\n return Item.EMPTY_ARRAY;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ConstantForZeroLengthArrayAllocation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Memory", + "index": 73, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LiteralAsArgToStringEquals", + "shortDescription": { + "text": "String literal may be 'equals()' qualifier" + }, + "fullDescription": { + "text": "Reports 'String.equals()' or 'String.equalsIgnoreCase()' calls with a string literal argument. Some coding standards specify that string literals should be the qualifier of 'equals()', rather than argument, thus minimizing 'NullPointerException'-s. A quick-fix is available to exchange the literal and the expression. Example: 'boolean isFoo(String value) {\n return value.equals(\"foo\");\n }' After the quick-fix is applied: 'boolean isFoo(String value) {\n return \"foo\".equals(value);\n }'", + "markdown": "Reports `String.equals()` or `String.equalsIgnoreCase()` calls with a string literal argument.\n\nSome coding standards specify that string literals should be the qualifier of `equals()`, rather than\nargument, thus minimizing `NullPointerException`-s.\n\nA quick-fix is available to exchange the literal and the expression.\n\n**Example:**\n\n\n boolean isFoo(String value) {\n return value.equals(\"foo\");\n }\n\nAfter the quick-fix is applied:\n\n\n boolean isFoo(String value) {\n return \"foo\".equals(value);\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LiteralAsArgToStringEquals", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DuplicateExpressions", + "shortDescription": { + "text": "Multiple occurrences of the same expression" + }, + "fullDescription": { + "text": "Reports multiple equivalent occurrences of the same expression within a method (or constructor, or class initializer) if the result of the expression can be reused. The expression is reported if it's free of side effects and its result is always the same (in terms of 'Object.equals()'). The examples of such expressions are 'a + b', 'Math.max(a, b)', 'a.equals(b)', 's.substring(a,b)'. To make sure the result is always the same, it's verified that the variables used in the expression don't change their values between the occurrences of the expression. Such expressions may contain methods of immutable classes like 'String', 'BigDecimal', and so on, and of utility classes like 'Objects', 'Math' (except 'random()'). The well-known methods, such as 'Object.equals()', 'Object.hashCode()', 'Object.toString()', 'Comparable.compareTo()', and 'Comparator.compare()' are OK as well because they normally don't have any observable side effects. Use the Expression complexity threshold option to specify the minimal expression complexity threshold. Specifying bigger numbers will remove reports on short expressions. 'Path.of' and 'Paths.get' calls are treated as equivalent calls if they have the same arguments. These calls are always reported no matter how complex their arguments are. This behaviour can be tweaked using different complexity threshold. New in 2018.3", + "markdown": "Reports multiple equivalent occurrences of the same expression within a method (or constructor, or class initializer) if the result of the expression can be reused.\n\n\nThe expression is reported if it's free of side effects and its result is always the same (in terms of `Object.equals()`).\nThe examples of such expressions are `a + b`, `Math.max(a, b)`, `a.equals(b)`,\n`s.substring(a,b)`. To make sure the result is always the same, it's verified that the variables used in the expression don't\nchange their values between the occurrences of the expression.\n\n\nSuch expressions may contain methods of immutable classes like `String`, `BigDecimal`, and so on,\nand of utility classes like `Objects`, `Math` (except `random()`).\nThe well-known methods, such as `Object.equals()`, `Object.hashCode()`, `Object.toString()`,\n`Comparable.compareTo()`, and `Comparator.compare()` are OK as well because they normally don't have\nany observable side effects.\n\n\nUse the **Expression complexity threshold** option to specify the minimal expression complexity threshold. Specifying bigger\nnumbers will remove reports on short expressions.\n\n\n`Path.of` and `Paths.get` calls are treated as equivalent calls if they have the same arguments. These calls\nare always reported no matter how complex their arguments are. This behaviour can be tweaked using different complexity threshold.\n\nNew in 2018.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "DuplicateExpressions", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryReturn", + "shortDescription": { + "text": "Unnecessary 'return' statement" + }, + "fullDescription": { + "text": "Reports 'return' statements at the end of constructors and methods returning 'void'. These statements are redundant and may be safely removed. This inspection does not report in JSP files. Example: 'void message() {\n System.out.println(\"Hello World\");\n return;\n }' After the quick-fix is applied: 'void message() {\n System.out.println(\"Hello World\");\n }' Use the Ignore in then branch of 'if' statement with 'else' branch option to ignore 'return' statements in the then branch of 'if' statements which also have an 'else' branch.", + "markdown": "Reports `return` statements at the end of constructors and methods returning `void`. These statements are redundant and may be safely removed.\n\nThis inspection does not report in JSP files.\n\nExample:\n\n\n void message() {\n System.out.println(\"Hello World\");\n return;\n }\n\nAfter the quick-fix is applied:\n\n\n void message() {\n System.out.println(\"Hello World\");\n }\n\n\nUse the **Ignore in then branch of 'if' statement with 'else' branch** option to ignore `return` statements in the then branch of `if` statements\nwhich also have an `else` branch." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryReturnStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PublicInnerClass", + "shortDescription": { + "text": "'public' nested class" + }, + "fullDescription": { + "text": "Reports 'public' nested classes. Example: 'public class Outer {\n public static class Nested {} // warning\n public class Inner {} // warning\n public enum Mode {} // warning depends on the setting\n public interface I {} // warning depends on the setting\n }' Configure the inspection: Use the Ignore 'public' inner enums option to ignore 'public' inner enums. Use the Ignore 'public' inner interfaces option to ignore 'public' inner interfaces.", + "markdown": "Reports `public` nested classes.\n\n**Example:**\n\n\n public class Outer {\n public static class Nested {} // warning\n public class Inner {} // warning\n public enum Mode {} // warning depends on the setting\n public interface I {} // warning depends on the setting\n }\n\nConfigure the inspection:\n\n* Use the **Ignore 'public' inner enums** option to ignore `public` inner enums.\n* Use the **Ignore 'public' inner interfaces** option to ignore `public` inner interfaces." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PublicInnerClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Encapsulation", + "index": 60, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BooleanConstructor", + "shortDescription": { + "text": "Boolean constructor call" + }, + "fullDescription": { + "text": "Reports creation of 'Boolean' objects. Constructing new 'Boolean' objects is rarely necessary, and may cause performance problems if done often enough. Also, 'Boolean' constructors are deprecated since Java 9 and could be removed or made inaccessible in future Java versions. Example: 'Boolean b1 = new Boolean(true);\n Boolean b2 = new Boolean(str);' After the quick-fix is applied: 'Boolean b1 = Boolean.TRUE;\n Boolean b2 = Boolean.valueOf(str);'", + "markdown": "Reports creation of `Boolean` objects.\n\n\nConstructing new `Boolean` objects is rarely necessary,\nand may cause performance problems if done often enough. Also, `Boolean`\nconstructors are deprecated since Java 9 and could be removed or made\ninaccessible in future Java versions.\n\n**Example:**\n\n\n Boolean b1 = new Boolean(true);\n Boolean b2 = new Boolean(str);\n\nAfter the quick-fix is applied:\n\n\n Boolean b1 = Boolean.TRUE;\n Boolean b2 = Boolean.valueOf(str);\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "BooleanConstructorCall", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonFinalGuard", + "shortDescription": { + "text": "Non-final '@GuardedBy' field" + }, + "fullDescription": { + "text": "Reports '@GuardedBy' annotations in which the guarding field is not 'final'. Guarding on a non-final field may result in unexpected race conditions, as locks will be held on the value of the field (which may change), rather than the field itself. Example: 'private ReadWriteLock lock = new ReentrantReadWriteLock(); //not final guarding field\n private Object state;\n\n @GuardedBy(\"lock\")\n public void bar() {\n state = new Object();\n }' Supported '@GuardedBy' annotations are: 'net.jcip.annotations.GuardedBy' 'javax.annotation.concurrent.GuardedBy' 'org.apache.http.annotation.GuardedBy' 'com.android.annotations.concurrency.GuardedBy' 'androidx.annotation.GuardedBy' 'com.google.errorprone.annotations.concurrent.GuardedBy'", + "markdown": "Reports `@GuardedBy` annotations in which the guarding field is not `final`.\n\nGuarding on a non-final field may result in unexpected race conditions, as locks will\nbe held on the value of the field (which may change), rather than the field itself.\n\nExample:\n\n\n private ReadWriteLock lock = new ReentrantReadWriteLock(); //not final guarding field\n private Object state;\n\n @GuardedBy(\"lock\")\n public void bar() {\n state = new Object();\n }\n\nSupported `@GuardedBy` annotations are:\n\n* `net.jcip.annotations.GuardedBy`\n* `javax.annotation.concurrent.GuardedBy`\n* `org.apache.http.annotation.GuardedBy`\n* `com.android.annotations.concurrency.GuardedBy`\n* `androidx.annotation.GuardedBy`\n* `com.google.errorprone.annotations.concurrent.GuardedBy`" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonFinalGuard", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Concurrency annotation issues", + "index": 61, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TooBroadScope", + "shortDescription": { + "text": "Scope of variable is too broad" + }, + "fullDescription": { + "text": "Reports any variable declarations that can be moved to a smaller scope. This inspection is especially useful for Pascal style declarations at the beginning of a method. Additionally variables with too broad a scope are also often left behind after refactorings. Example: 'StringBuilder sb = new StringBuilder();\n System.out.println();\n sb.append(1);' After the quick-fix is applied: 'System.out.println();\n StringBuilder sb = new StringBuilder();\n sb.append(1);' Configure the inspection: Use the Only report variables that can be moved into inner blocks option to report only those variables that can be moved inside deeper code blocks. For example, when the option is enabled, the movement will not be suggested for the 'sb' variable above. However, it will be suggested for the following code: 'StringBuilder sb = new StringBuilder(a);\n if (flag) {\n sb.append(1);\n }' Use the Report variables with a new expression as initializer (potentially unsafe) option to report variables that are initialized with a new expression. This makes the inspection potentially unsafe when the constructor has non-local side effects. For example, when the option is enabled, the movement will be suggested for the 'foo' variable: 'class Foo {\n static List fooList = new ArrayList<>();\n String bar;\n\n Foo(String bar) {\n this.bar = bar;\n fooList.add(this);\n }\n\n public static void main(String[] args) {\n // movement is possible even though is unsafe\n Foo foo = new Foo(\"bar\");\n System.out.println(fooList.size());\n System.out.println(foo.bar);\n }\n }'", + "markdown": "Reports any variable declarations that can be moved to a smaller scope.\n\nThis inspection is especially\nuseful for *Pascal style* declarations at the beginning of a method. Additionally variables with too broad a\nscope are also often left behind after refactorings.\n\n**Example:**\n\n\n StringBuilder sb = new StringBuilder();\n System.out.println();\n sb.append(1);\n\nAfter the quick-fix is applied:\n\n\n System.out.println();\n StringBuilder sb = new StringBuilder();\n sb.append(1);\n\nConfigure the inspection:\n\n* Use the **Only report variables that can be moved into inner blocks** option to report only those variables that can be moved inside deeper code blocks. For example, when the option is enabled, the movement will not be suggested for the `sb` variable above. However, it will be suggested for the following code:\n\n\n StringBuilder sb = new StringBuilder(a);\n if (flag) {\n sb.append(1);\n }\n\n* Use the **Report variables with a new expression as initializer\n (potentially unsafe)** option to report variables that are initialized with a new expression. This makes the inspection potentially unsafe when the constructor has non-local side effects. For example, when the option is enabled, the movement will be suggested for the `foo` variable:\n\n\n class Foo {\n static List fooList = new ArrayList<>();\n String bar;\n\n Foo(String bar) {\n this.bar = bar;\n fooList.add(this);\n }\n\n public static void main(String[] args) {\n // movement is possible even though is unsafe\n Foo foo = new Foo(\"bar\");\n System.out.println(fooList.size());\n System.out.println(foo.bar);\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "TooBroadScope", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Data flow", + "index": 23, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CollectionAddedToSelf", + "shortDescription": { + "text": "Collection added to itself" + }, + "fullDescription": { + "text": "Reports cases where the argument of a method call on a 'java.util.Collection' or 'java.util.Map' is the collection or map itself. Such situations may occur as a result of copy-paste in code with raw types. Example: 'ArrayList list = new ArrayList<>();\n list.add(list); // warning here\n return list.hashCode(); // throws StackOverflowError'", + "markdown": "Reports cases where the argument of a method call on a `java.util.Collection` or `java.util.Map` is the collection or map itself. Such situations may occur as a result of copy-paste in code with raw types.\n\n**Example:**\n\n\n ArrayList list = new ArrayList<>();\n list.add(list); // warning here\n return list.hashCode(); // throws StackOverflowError\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "CollectionAddedToSelf", + "cweIds": [ + 664, + 688 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ThrowableSupplierOnlyThrowException", + "shortDescription": { + "text": "Throwable supplier never returns a value" + }, + "fullDescription": { + "text": "Reports 'Supplier' lambdas in 'Optional.orElseThrow()' calls that throw an exception, instead of returning it. Example: 'optional.orElseThrow(() -> {\n throw new RuntimeException();\n});' After the quick-fix is applied: 'optional.orElseThrow(() -> new RuntimeException());' New in 2023.1", + "markdown": "Reports `Supplier` lambdas in `Optional.orElseThrow()` calls that throw an exception, instead of returning it.\n\n**Example:**\n\n\n optional.orElseThrow(() -> {\n throw new RuntimeException();\n });\n\nAfter the quick-fix is applied:\n\n\n optional.orElseThrow(() -> new RuntimeException());\n\nNew in 2023.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ThrowableSupplierOnlyThrowException", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IfStatementMissingBreakInLoop", + "shortDescription": { + "text": "Early loop exit in 'if' condition" + }, + "fullDescription": { + "text": "Reports loops with an 'if' statement that can end with 'break' without changing the semantics. This prevents redundant loop iterations. Example: 'boolean found = false;\n for (int i = 0; i < arr.length; i++) {\n if (Objects.equals(value, arr[i])) {\n found = true;\n }\n }' After the quick-fix is applied: 'boolean found = false;\n for (int i = 0; i < arr.length; i++) {\n if (Objects.equals(value, arr[i])) {\n found = true;\n break;\n }\n }' New in 2019.2", + "markdown": "Reports loops with an `if` statement that can end with `break` without changing the semantics. This prevents redundant loop iterations.\n\n**Example:**\n\n\n boolean found = false;\n for (int i = 0; i < arr.length; i++) {\n if (Objects.equals(value, arr[i])) {\n found = true;\n }\n }\n\nAfter the quick-fix is applied:\n\n\n boolean found = false;\n for (int i = 0; i < arr.length; i++) {\n if (Objects.equals(value, arr[i])) {\n found = true;\n break;\n }\n }\n\nNew in 2019.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "IfStatementMissingBreakInLoop", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantStreamOptionalCall", + "shortDescription": { + "text": "Redundant step in 'Stream' or 'Optional' call chain" + }, + "fullDescription": { + "text": "Reports redundant 'Stream' or 'Optional' calls like 'map(x -> x)', 'filter(x -> true)' or redundant 'sorted()' or 'distinct()' calls. Note that a mapping operation in code like 'streamOfIntegers.map(Integer::valueOf)' works as 'requireNonNull()' check: if the stream contains 'null', it throws a 'NullPointerException', thus it's not absolutely redundant. Disable the Report redundant boxing in Stream.map() option if you do not want such cases to be reported. This inspection only reports if the language level of the project or module is 8 or higher.", + "markdown": "Reports redundant `Stream` or `Optional` calls like `map(x -> x)`, `filter(x -> true)` or redundant `sorted()` or `distinct()` calls.\n\nNote that a mapping operation in code like `streamOfIntegers.map(Integer::valueOf)`\nworks as `requireNonNull()` check:\nif the stream contains `null`, it throws a `NullPointerException`, thus it's not absolutely redundant.\nDisable the **Report redundant boxing in Stream.map()** option if you do not want such cases to be reported.\n\nThis inspection only reports if the language level of the project or module is 8 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantStreamOptionalCall", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessarySuperQualifier", + "shortDescription": { + "text": "Unnecessary 'super' qualifier" + }, + "fullDescription": { + "text": "Reports unnecessary 'super' qualifiers in method calls and field references. A 'super' qualifier is unnecessary when the field or method of the superclass is not hidden/overridden in the calling class. Example: 'class Foo {\n void foo() {}\n }\n\n class Bar extends Foo {\n void bar() {\n super.foo();\n }\n }' After the quick-fix is applied: 'class Foo {\n void foo() {}\n }\n\n class Bar extends Foo {\n void bar() {\n foo();\n }\n }' Use the inspection settings to ignore qualifiers that help to distinguish superclass members access from the identically named members of the outer class. See also the following inspections: Java | Visibility | Access to inherited field looks like access to element from surrounding code Java | Visibility | Call to inherited method looks like call to local method", + "markdown": "Reports unnecessary `super` qualifiers in method calls and field references.\n\n\nA `super` qualifier is unnecessary\nwhen the field or method of the superclass is not hidden/overridden in the calling class.\n\n**Example:**\n\n\n class Foo {\n void foo() {}\n }\n\n class Bar extends Foo {\n void bar() {\n super.foo();\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo {\n void foo() {}\n }\n\n class Bar extends Foo {\n void bar() {\n foo();\n }\n }\n\n\nUse the inspection settings to ignore qualifiers that help to distinguish superclass members access\nfrom the identically named members of the outer class.\n\n\nSee also the following inspections:\n\n* *Java \\| Visibility \\| Access to inherited field looks like access to element from surrounding code*\n* *Java \\| Visibility \\| Call to inherited method looks like call to local method*" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessarySuperQualifier", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ThreadPriority", + "shortDescription": { + "text": "Call to 'Thread.setPriority()'" + }, + "fullDescription": { + "text": "Reports calls to 'Thread.setPriority()'. Modifying priorities of threads is an inherently non-portable operation, as no guarantees are given in the Java specification of how priorities are used in scheduling threads, or even whether they are used at all.", + "markdown": "Reports calls to `Thread.setPriority()`. Modifying priorities of threads is an inherently non-portable operation, as no guarantees are given in the Java specification of how priorities are used in scheduling threads, or even whether they are used at all." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToThreadSetPriority", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EqualsOnSuspiciousObject", + "shortDescription": { + "text": "'equals()' called on classes which don't override it" + }, + "fullDescription": { + "text": "Reports 'equals()' calls on 'StringBuilder', 'StringBuffer' and instances of 'java.util.concurrent.atomic' package. The 'equals()' method is not overridden in these classes, so it may return 'false' even when the contents of the two objects are the same. If the reference equality is intended, it's better to use '==' to avoid confusion. A quick-fix for 'StringBuilder', 'StringBuffer', 'AtomicBoolean', 'AtomicInteger', 'AtomicBoolean' and 'AtomicLong' is available to transform into a comparison of contents. The quick-fix may change the semantics when one of the instances is null. Example: 'public void test(StringBuilder sb1, StringBuilder sb2) {\n boolean result = sb1.equals(sb2); // Suspicious\n }' After the quick-fix is applied: 'public void test(StringBuilder sb1, StringBuilder sb2) {\n boolean result = sb1.toString().equals(sb2.toString());\n }' New in 2017.2", + "markdown": "Reports `equals()` calls on `StringBuilder`, `StringBuffer` and instances of `java.util.concurrent.atomic` package.\n\nThe `equals()` method is not overridden in these classes, so it may return `false` even when the contents of the\ntwo objects are the same.\nIf the reference equality is intended, it's better to use `==` to avoid confusion.\nA quick-fix for `StringBuilder`, `StringBuffer`, `AtomicBoolean`, `AtomicInteger`, `AtomicBoolean` and `AtomicLong` is available to transform into a comparison of contents. The quick-fix may change the semantics when one of the instances is null.\n\nExample:\n\n\n public void test(StringBuilder sb1, StringBuilder sb2) {\n boolean result = sb1.equals(sb2); // Suspicious\n }\n\nAfter the quick-fix is applied:\n\n\n public void test(StringBuilder sb1, StringBuilder sb2) {\n boolean result = sb1.toString().equals(sb2.toString());\n }\n\nNew in 2017.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "EqualsOnSuspiciousObject", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JUnitMalformedDeclaration", + "shortDescription": { + "text": "JUnit malformed declaration" + }, + "fullDescription": { + "text": "Reports JUnit test member declarations that are malformed and are likely not recognized by the JUnit test framework. The following problems are reported by this inspection: Test classes that can't be constructed Fields annotated by '@RegisterExtension' that have the wrong type or are not declared as static when it is required Static or private inner classes annotated with '@Nested' Parameterized tests that are defined without a source Parameterized tests with a '@MethodSource' that has an unknown, non-static or no-arg target Mismatched types between parameterized test method parameter and the specified '@ValueSource' or '@EnumSource' values Tests that are annotated by more than one of '@Test', '@ParameterizedTest' or '@RepeatedTest' 'setup()' or 'tearDown()' methods that are not public, whose return type is not void or take arguments 'suite()' methods that are private, take arguments or are not static Methods annotated by '@BeforeClass', '@AfterClass', '@BeforeAll' or '@AfterAll' that are not public, not static, whose return type is not void or do not have a valid parameter list Methods annotated by '@Before', '@After', '@BeforeEach' or '@AfterEach' that are not public, whose return type is not void or take arguments Injected 'RepetitionInfo' in '@BeforeAll' or '@AfterAll' methods Injected 'RepetitionInfo' in '@BeforeEach' or '@AfterEach' methods that are used by '@Test' annotated tests Fields and methods annotated by '@DataPoint' or '@DataPoints' that are not public or not static Fields and methods annotated by '@Rule' that are not public or not a subtype of 'TestRule' or 'MethodRule' Fields and methods annotated by '@ClassRule' that are not public, not static or not a subtype of 'TestRule' Methods inside a subclass of 'TestCase' with a 'test' prefix that are not public, whose return type is not void, take arguments or are static Methods annotated by '@Test' that are not public, whose return type is not void, take arguments or are static Note that in Kotlin, suspending functions do have arguments and a non-void return type. Therefore, they also will not be executed by the JUnit test runner. This inspection will also report about this problem. Malformed '@Before' method example (Java): '@Before private int foo(int arg) { ... }' After the quick-fix is applied: '@Before public void foo() { ... }' Missing method source example (Kotlin): 'class Example {\n @MethodSource(\"parameters\")\n @ParameterizedTest\n fun foo(param: String) { ... }\n }' After the quick-fix is applied: 'class Example {\n @MethodSource(\"parameters\")\n @ParameterizedTest\n fun foo(param: String) { ... }\n\n companion object {\n @JvmStatic\n fun parameters(): Stream {\n TODO(\"Not yet implemented\")\n }\n }\n }' Use the inspection options to specify annotations. Any parameter annotated with one of these annotations will not be reported.", + "markdown": "Reports JUnit test member declarations that are malformed and are likely not recognized by the JUnit test framework. The following problems are reported by this inspection:\n\n* Test classes that can't be constructed\n* Fields annotated by `@RegisterExtension` that have the wrong type or are not declared as static when it is required\n* Static or private inner classes annotated with `@Nested`\n* Parameterized tests that are defined without a source\n* Parameterized tests with a `@MethodSource` that has an unknown, non-static or no-arg target\n* Mismatched types between parameterized test method parameter and the specified `@ValueSource` or `@EnumSource` values\n* Tests that are annotated by more than one of `@Test`, `@ParameterizedTest` or `@RepeatedTest`\n* `setup()` or `tearDown()` methods that are not public, whose return type is not void or take arguments\n* `suite()` methods that are private, take arguments or are not static\n* Methods annotated by `@BeforeClass`, `@AfterClass`, `@BeforeAll` or `@AfterAll` that are not public, not static, whose return type is not void or do not have a valid parameter list\n* Methods annotated by `@Before`, `@After`, `@BeforeEach` or `@AfterEach` that are not public, whose return type is not void or take arguments\n* Injected `RepetitionInfo` in `@BeforeAll` or `@AfterAll` methods\n* Injected `RepetitionInfo` in `@BeforeEach` or `@AfterEach` methods that are used by `@Test` annotated tests\n* Fields and methods annotated by `@DataPoint` or `@DataPoints` that are not public or not static\n* Fields and methods annotated by `@Rule` that are not public or not a subtype of `TestRule` or `MethodRule`\n* Fields and methods annotated by `@ClassRule` that are not public, not static or not a subtype of `TestRule`\n* Methods inside a subclass of `TestCase` with a `test` prefix that are not public, whose return type is not void, take arguments or are static\n* Methods annotated by `@Test` that are not public, whose return type is not void, take arguments or are static\n\nNote that in Kotlin, suspending functions do have arguments and a non-void return type. Therefore, they also will not be executed by the JUnit test runner. This inspection will also report about this problem.\n\n**Malformed `@Before` method example (Java):**\n\n @Before private int foo(int arg) { ... } \n\nAfter the quick-fix is applied:\n\n @Before public void foo() { ... } \n\n**Missing method source example (Kotlin):**\n\n\n class Example {\n @MethodSource(\"parameters\")\n @ParameterizedTest\n fun foo(param: String) { ... }\n }\n\nAfter the quick-fix is applied:\n\n\n class Example {\n @MethodSource(\"parameters\")\n @ParameterizedTest\n fun foo(param: String) { ... }\n\n companion object {\n @JvmStatic\n fun parameters(): Stream {\n TODO(\"Not yet implemented\")\n }\n }\n }\n\nUse the inspection options to specify annotations. Any parameter annotated with one of these annotations will not be reported." + }, + "defaultConfiguration": { + "enabled": true, + "level": "error", + "parameters": { + "suppressToolId": "JUnitMalformedDeclaration", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages/Test frameworks", + "index": 94, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UseOfPropertiesAsHashtable", + "shortDescription": { + "text": "Use of 'Properties' object as a 'Hashtable'" + }, + "fullDescription": { + "text": "Reports calls to the following methods on 'java.util.Properties' objects: 'put()' 'putIfAbsent()' 'putAll()' 'get()' For historical reasons, 'java.util.Properties' inherits from 'java.util.Hashtable', but using these methods is discouraged to prevent pollution of properties with values of types other than 'String'. Calls to 'java.util.Properties.putAll()' won't get reported when both the key and the value parameters in the map are of the 'String' type. Such a call is safe and no better alternative exists. Example: 'Object f(Properties props) {\n props.put(\"hello\", \"world\");\n props.putIfAbsent(\"hello\", \"world\");\n props.putAll(new HashMap<>());\n return props.get(\"Hello\");\n }' After the quick-fix is applied: 'Object f(Properties props) {\n props.setProperty(\"hello\", \"world\");\n props.putIfAbsent(\"hello\", \"world\");\n props.putAll(new HashMap<>());\n return props.getProperty(\"hello\");\n }'", + "markdown": "Reports calls to the following methods on `java.util.Properties` objects:\n\n* `put()`\n* `putIfAbsent()`\n* `putAll()`\n* `get()`\n\n\nFor historical reasons, `java.util.Properties` inherits from `java.util.Hashtable`,\nbut using these methods is discouraged to prevent pollution of properties with values of types other than `String`.\n\n\nCalls to `java.util.Properties.putAll()` won't get reported when\nboth the key and the value parameters in the map are of the `String` type.\nSuch a call is safe and no better alternative exists.\n\n**Example:**\n\n\n Object f(Properties props) {\n props.put(\"hello\", \"world\");\n props.putIfAbsent(\"hello\", \"world\");\n props.putAll(new HashMap<>());\n return props.get(\"Hello\");\n }\n\nAfter the quick-fix is applied:\n\n\n Object f(Properties props) {\n props.setProperty(\"hello\", \"world\");\n props.putIfAbsent(\"hello\", \"world\");\n props.putAll(new HashMap<>());\n return props.getProperty(\"hello\");\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UseOfPropertiesAsHashtable", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MethodCoupling", + "shortDescription": { + "text": "Overly coupled method" + }, + "fullDescription": { + "text": "Reports methods that reference too many other classes. Methods with too high coupling can be very fragile and should be probably split into smaller methods. Each referenced class is counted only once no matter how many times it is referenced. Configure the inspection: Use the Method coupling limit field to specify the maximum allowed coupling for a method. Use the Include couplings to java system classes option to count references to classes from 'java'or 'javax' packages. Use the Include couplings to library classes option to count references to third-party library classes.", + "markdown": "Reports methods that reference too many other classes. Methods with too high coupling can be very fragile and should be probably split into smaller methods.\n\nEach referenced class is counted only once no matter how many times it is referenced.\n\nConfigure the inspection:\n\n* Use the **Method coupling limit** field to specify the maximum allowed coupling for a method.\n* Use the **Include couplings to java system classes** option to count references to classes from `java`or `javax` packages.\n* Use the **Include couplings to library classes** option to count references to third-party library classes." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OverlyCoupledMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Method metrics", + "index": 95, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonFinalFieldOfException", + "shortDescription": { + "text": "Non-final field of 'Exception' class" + }, + "fullDescription": { + "text": "Reports fields in subclasses of 'java.lang.Exception' that are not declared 'final'. Data on exception objects should not be modified because this may result in losing the error context for later debugging and logging. Example: 'public class EditorException extends Exception {\n private String message; // warning: Non-final field 'message' of exception class\n }'", + "markdown": "Reports fields in subclasses of `java.lang.Exception` that are not declared `final`.\n\nData on exception objects should not be modified\nbecause this may result in losing the error context for later debugging and logging.\n\n**Example:**\n\n\n public class EditorException extends Exception {\n private String message; // warning: Non-final field 'message' of exception class\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonFinalFieldOfException", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantStringFormatCall", + "shortDescription": { + "text": "Redundant call to 'String.format()'" + }, + "fullDescription": { + "text": "Reports calls to methods like 'format()' and 'printf()' that can be safely removed or simplified. Example: 'System.out.println(String.format(\"Total count: %d\", 42));' After the quick-fix is applied: 'System.out.printf(\"Total count: %d%n\", 42);'", + "markdown": "Reports calls to methods like `format()` and `printf()` that can be safely removed or simplified.\n\n**Example:**\n\n\n System.out.println(String.format(\"Total count: %d\", 42));\n\nAfter the quick-fix is applied:\n\n\n System.out.printf(\"Total count: %d%n\", 42);\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantStringFormatCall", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PointlessNullCheck", + "shortDescription": { + "text": "Unnecessary 'null' check before method call" + }, + "fullDescription": { + "text": "Reports 'null' checks followed by a method call that will definitely return 'false' when 'null' is passed (e.g. 'Class.isInstance'). Such a check seems excessive as the method call will always return 'false' in this case. Example: 'if (x != null && myClass.isInstance(x)) { ... }' After the quick-fix is applied: 'if (myClass.isInstance(x)) { ... }'", + "markdown": "Reports `null` checks followed by a method call that will definitely return `false` when `null` is passed (e.g. `Class.isInstance`).\n\nSuch a check seems excessive as the method call will always return `false` in this case.\n\n**Example:**\n\n\n if (x != null && myClass.isInstance(x)) { ... }\n\nAfter the quick-fix is applied:\n\n\n if (myClass.isInstance(x)) { ... }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "PointlessNullCheck", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AccessToStaticFieldLockedOnInstance", + "shortDescription": { + "text": "Access to 'static' field locked on instance data" + }, + "fullDescription": { + "text": "Reports access to non-constant static fields that are locked on either 'this' or an instance field of 'this'. Locking a static field on instance data does not prevent the field from being modified by other instances, and thus may result in unexpected race conditions. Example: 'static String test;\n public void foo() {\n synchronized (this) {\n System.out.println(test); // warning\n }\n }' There is a quick-fix that allows ignoring static fields of specific types. You can manage those ignored types in the inspection options. Use the inspection options to specify which classes used for static fields should be ignored.", + "markdown": "Reports access to non-constant static fields that are locked on either `this` or an instance field of `this`.\n\n\nLocking a static field on instance data does not prevent the field from being\nmodified by other instances, and thus may result in unexpected race conditions.\n\n**Example:**\n\n\n static String test;\n public void foo() {\n synchronized (this) {\n System.out.println(test); // warning\n }\n }\n\n\nThere is a quick-fix that allows ignoring static fields of specific types.\nYou can manage those ignored types in the inspection options.\n\n\nUse the inspection options to specify which classes used for static fields should be ignored." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AccessToStaticFieldLockedOnInstance", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MethodOverridesStaticMethod", + "shortDescription": { + "text": "Method tries to override 'static' method of superclass" + }, + "fullDescription": { + "text": "Reports 'static' methods with a signature identical to a 'static' method of a superclass. Such a method may look like an override when in fact it hides the method from the superclass because 'static' methods in Java cannot be overridden. Example: 'class Parent {\n static void method(){}\n }\n\n class Example extends Parent {\n static void method(){} //warning\n }'", + "markdown": "Reports `static` methods with a signature identical to a `static` method of a superclass. Such a method may look like an override when in fact it hides the method from the superclass because `static` methods in Java cannot be overridden.\n\n**Example:**\n\n\n class Parent {\n static void method(){}\n }\n\n class Example extends Parent {\n static void method(){} //warning\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MethodOverridesStaticMethodOfSuperclass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Visibility", + "index": 57, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnclearBinaryExpression", + "shortDescription": { + "text": "Multiple operators with different precedence" + }, + "fullDescription": { + "text": "Reports binary, conditional, or 'instanceof' expressions that consist of different operators without parentheses. Such expressions can be less readable due to different precedence rules of operators. Example: 'int n = 3 + 9 * 8 + 1;' After quick-fix is applied: 'int n = 3 + (9 * 8) + 1;'", + "markdown": "Reports binary, conditional, or `instanceof` expressions that consist of different operators without parentheses. Such expressions can be less readable due to different precedence rules of operators.\n\nExample:\n\n\n int n = 3 + 9 * 8 + 1;\n\nAfter quick-fix is applied:\n\n\n int n = 3 + (9 * 8) + 1;\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "UnclearExpression", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantCompareToJavaTime", + "shortDescription": { + "text": "Expression with 'java.time' 'compareTo()' call can be simplified" + }, + "fullDescription": { + "text": "Reports 'java.time' comparisons with 'compareTo()' calls that can be replaced with 'isAfter()', 'isBefore()' or 'isEqual()' calls. Example: 'LocalDate date1 = LocalDate.now();\n LocalDate date2 = LocalDate.now();\n boolean t = date1.compareTo(date2) > 0;' After the quick-fix is applied: 'LocalDate date1 = LocalDate.now();\n LocalDate date2 = LocalDate.now();\n boolean t = date1.isAfter(date2);' New in 2022.3", + "markdown": "Reports `java.time` comparisons with `compareTo()` calls that can be replaced with `isAfter()`, `isBefore()` or `isEqual()` calls.\n\nExample:\n\n\n LocalDate date1 = LocalDate.now();\n LocalDate date2 = LocalDate.now();\n boolean t = date1.compareTo(date2) > 0;\n\nAfter the quick-fix is applied:\n\n\n LocalDate date1 = LocalDate.now();\n LocalDate date2 = LocalDate.now();\n boolean t = date1.isAfter(date2);\n\nNew in 2022.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantCompareToJavaTime", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ChainedMethodCall", + "shortDescription": { + "text": "Chained method calls" + }, + "fullDescription": { + "text": "Reports method calls whose target is another method call. The quick-fix suggests to introduce a local variable. Example: 'class X {\n int foo(File f) {\n return f.getName().length();\n }\n }' After the quick-fix is applied: 'class X {\n int foo(File f) {\n final String name = f.getName();\n return name.length();\n }\n }' Use the inspection options to toggle warnings for the following cases: chained method calls in field initializers, for instance, 'private final int i = new Random().nextInt();' chained method calls operating on the same type, for instance, 'new StringBuilder().append(\"x: \").append(new X()).append(\"y: \").append(new Y()).toString();'.", + "markdown": "Reports method calls whose target is another method call. The quick-fix suggests to introduce a local variable.\n\n**Example:**\n\n\n class X {\n int foo(File f) {\n return f.getName().length();\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class X {\n int foo(File f) {\n final String name = f.getName();\n return name.length();\n }\n }\n\nUse the inspection options to toggle warnings for the following cases:\n\n*\n chained method calls in field initializers,\n for instance, `private final int i = new Random().nextInt();`\n\n*\n chained method calls operating on the same type,\n for instance, `new StringBuilder().append(\"x: \").append(new X()).append(\"y: \").append(new Y()).toString();`." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ChainedMethodCall", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "VariableTypeCanBeExplicit", + "shortDescription": { + "text": "Variable type can be explicit" + }, + "fullDescription": { + "text": "Reports local variables of the 'var' type that can be replaced with an explicit type. Example: 'var str = \"Hello\";' After the quick-fix is applied: 'String str = \"Hello\";' 'var' keyword appeared in Java 10. This inspection can help to downgrade for backward compatibility with earlier Java versions.", + "markdown": "Reports local variables of the `var` type that can be replaced with an explicit type.\n\n**Example:**\n\n\n var str = \"Hello\";\n\nAfter the quick-fix is applied:\n\n\n String str = \"Hello\";\n\n\n`var` *keyword* appeared in Java 10.\nThis inspection can help to downgrade for backward compatibility with earlier Java versions." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "VariableTypeCanBeExplicit", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 10", + "index": 110, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ExtendsUtilityClass", + "shortDescription": { + "text": "Class extends utility class" + }, + "fullDescription": { + "text": "Reports classes that extend a utility class. A utility class is a non-empty class in which all fields and methods are static. Extending a utility class also allows for inadvertent object instantiation of the utility class, because the constructor cannot be made private in order to allow extension. Configure the inspection: Use the Ignore if overriding class is a utility class option to ignore any classes that override a utility class but are also utility classes themselves.", + "markdown": "Reports classes that extend a utility class.\n\n\nA utility class is a non-empty class in which all fields and methods are static.\nExtending a utility class also allows for inadvertent object instantiation of the\nutility class, because the constructor cannot be made private in order to allow extension.\n\n\nConfigure the inspection:\n\n* Use the **Ignore if overriding class is a utility class** option to ignore any classes that override a utility class but are also utility classes themselves." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ExtendsUtilityClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Inheritance issues", + "index": 25, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UtilityClassWithoutPrivateConstructor", + "shortDescription": { + "text": "Utility class without 'private' constructor" + }, + "fullDescription": { + "text": "Reports utility classes without 'private' constructors. Utility classes have all fields and methods declared as 'static'. Creating 'private' constructors in utility classes prevents them from being accidentally instantiated. Use the Ignore if annotated by option to specify special annotations. The inspection ignores classes marked with one of these annotations. Use the Ignore classes with only a main method option to ignore classes with no methods other than the main one.", + "markdown": "Reports utility classes without `private` constructors.\n\nUtility classes have all fields and methods declared as `static`. Creating `private`\nconstructors in utility classes prevents them from being accidentally instantiated.\n\n\nUse the **Ignore if annotated by** option to specify special annotations. The inspection ignores classes marked with one of\nthese annotations.\n\n\nUse the **Ignore classes with only a main method** option to ignore classes with no methods other than the main one." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UtilityClassWithoutPrivateConstructor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java8ListSort", + "shortDescription": { + "text": "'Collections.sort()' can be replaced with 'List.sort()'" + }, + "fullDescription": { + "text": "Reports calls of 'Collections.sort(list, comparator)' which can be replaced with 'list.sort(comparator)'. 'Collections.sort' is just a wrapper, so it is better to use an instance method directly. This inspection only reports if the language level of the project or module is 8 or higher.", + "markdown": "Reports calls of `Collections.sort(list, comparator)` which can be replaced with `list.sort(comparator)`.\n\n`Collections.sort` is just a wrapper, so it is better to use an instance method directly.\n\nThis inspection only reports if the language level of the project or module is 8 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "Java8ListSort", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 8", + "index": 62, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AssertEqualsCalledOnArray", + "shortDescription": { + "text": "'assertEquals()' called on array" + }, + "fullDescription": { + "text": "Reports JUnit 'assertEquals()' calls with arguments of an array type. Such methods compare the arrays' identities instead of the arrays' contents. Array contents should be checked with the 'assertArrayEquals()' method. Example: '@Test\n public void testSort() {\n int[] actual = {248, 496, 0, 56};\n Arrays.sort(actual);\n Assert.assertEquals(new int[] {0, 56, 248, 496}, actual);\n }' After the quick-fix is applied: '@Test\n public void testSort() {\n int[] actual = {248, 496, 0, 56};\n Arrays.sort(actual);\n Assert.assertArrayEquals(new int[] {0, 56, 248, 496}, actual);\n }'", + "markdown": "Reports JUnit `assertEquals()` calls with arguments of an array type. Such methods compare the arrays' identities instead of the arrays' contents. Array contents should be checked with the `assertArrayEquals()` method.\n\n**Example:**\n\n\n @Test\n public void testSort() {\n int[] actual = {248, 496, 0, 56};\n Arrays.sort(actual);\n Assert.assertEquals(new int[] {0, 56, 248, 496}, actual);\n }\n\nAfter the quick-fix is applied:\n\n\n @Test\n public void testSort() {\n int[] actual = {248, 496, 0, 56};\n Arrays.sort(actual);\n Assert.assertArrayEquals(new int[] {0, 56, 248, 496}, actual);\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AssertEqualsCalledOnArray", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages/Test frameworks", + "index": 94, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AssertMessageNotString", + "shortDescription": { + "text": "'assert' message is not a string" + }, + "fullDescription": { + "text": "Reports 'assert' messages that are not of the 'java.lang.String' type. Using a string provides more information to help diagnose the failure or the assertion reason. Example: 'void foo(List myList) {\n assert myList.isEmpty() : false;\n }' Use the Only warn when the 'assert' message type is 'boolean' or 'java.lang.Boolean' option to only warn when the 'assert' message type is 'boolean' or 'java.lang.Boolean'. A 'boolean' detail message is unlikely to provide additional information about an assertion failure and could result from a mistakenly entered ':' instead of '&'.", + "markdown": "Reports `assert` messages that are not of the `java.lang.String` type.\n\nUsing a string provides more information to help diagnose the failure\nor the assertion reason.\n\n**Example:**\n\n\n void foo(List myList) {\n assert myList.isEmpty() : false;\n }\n\n\nUse the **Only warn when the `assert` message type is 'boolean' or 'java.lang.Boolean'** option to only warn when the `assert` message type is `boolean` or `java.lang.Boolean`.\nA `boolean` detail message is unlikely to provide additional information about an assertion failure\nand could result from a mistakenly entered `:` instead of `&`." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AssertMessageNotString", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ComparatorCombinators", + "shortDescription": { + "text": "'Comparator' combinator can be used" + }, + "fullDescription": { + "text": "Reports 'Comparator' instances defined as lambda expressions that could be expressed using 'Comparator.comparing()' calls. Chained comparisons which can be replaced by 'Comparator.thenComparing()' expression are also reported. Example: 'myList.sort((person1, person2) -> person1.getName().compareTo(person2.getName()));\n\n myList2.sort((person1, person2) -> {\n int res = person1.first().compareTo(person2.first());\n if(res == 0) res = person1.second().compareTo(person2.second());\n if(res == 0) res = person1.third() - person2.third();\n return res;\n });' After the quick-fixes are applied: 'myList.sort(Comparator.comparing(Person::getName));\n\n myList2.sort(Comparator.comparing(Person::first)\n .thenComparing(Person::second)\n .thenComparingInt(Person::third));'", + "markdown": "Reports `Comparator` instances defined as lambda expressions that could be expressed using `Comparator.comparing()` calls. Chained comparisons which can be replaced by `Comparator.thenComparing()` expression are also reported.\n\nExample:\n\n\n myList.sort((person1, person2) -> person1.getName().compareTo(person2.getName()));\n\n myList2.sort((person1, person2) -> {\n int res = person1.first().compareTo(person2.first());\n if(res == 0) res = person1.second().compareTo(person2.second());\n if(res == 0) res = person1.third() - person2.third();\n return res;\n });\n\nAfter the quick-fixes are applied:\n\n\n myList.sort(Comparator.comparing(Person::getName));\n\n myList2.sort(Comparator.comparing(Person::first)\n .thenComparing(Person::second)\n .thenComparingInt(Person::third));\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ComparatorCombinators", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 8", + "index": 62, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PatternVariableCanBeUsed", + "shortDescription": { + "text": "Pattern variable can be used" + }, + "fullDescription": { + "text": "Reports local variable declarations that can be replaced with pattern variables, which are usually more compact. Example: 'if (obj instanceof String) {\n String str = (String) obj;\n System.out.println(str);\n }' Can be replaced with: 'if (obj instanceof String str) {\n System.out.println(str);\n }' This inspection only reports if the language level of the project or module is 16 or higher New in 2020.1", + "markdown": "Reports local variable declarations that can be replaced with pattern variables, which are usually more compact.\n\n**Example:**\n\n\n if (obj instanceof String) {\n String str = (String) obj;\n System.out.println(str);\n }\n\nCan be replaced with:\n\n\n if (obj instanceof String str) {\n System.out.println(str);\n }\n\nThis inspection only reports if the language level of the project or module is 16 or higher\n\nNew in 2020.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PatternVariableCanBeUsed", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 16", + "index": 111, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EqualsReplaceableByObjectsCall", + "shortDescription": { + "text": "'equals()' expression replaceable by 'Objects.equals()' expression" + }, + "fullDescription": { + "text": "Reports expressions that can be replaced with a call to 'java.util.Objects#equals'. Example: 'void f(Object a, Object b) {\n boolean result = a != null && a.equals(b);\n }' After the quick-fix is applied: 'void f(Object a, Object b) {\n boolean result = Objects.equals(a, b);\n }' Replacing expressions like 'a != null && a.equals(b)' with 'Objects.equals(a, b)' slightly changes the semantics. Use the Highlight expressions like 'a != null && a.equals(b)' option to enable or disable this behavior. This inspection only reports if the language level of the project or module is 7 or higher.", + "markdown": "Reports expressions that can be replaced with a call to `java.util.Objects#equals`.\n\n**Example:**\n\n\n void f(Object a, Object b) {\n boolean result = a != null && a.equals(b);\n }\n\nAfter the quick-fix is applied:\n\n\n void f(Object a, Object b) {\n boolean result = Objects.equals(a, b);\n }\n\n\nReplacing expressions like `a != null && a.equals(b)` with `Objects.equals(a, b)`\nslightly changes the semantics. Use the **Highlight expressions like 'a != null \\&\\& a.equals(b)'** option to enable or disable this behavior.\n\nThis inspection only reports if the language level of the project or module is 7 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "EqualsReplaceableByObjectsCall", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 7", + "index": 112, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AbstractMethodCallInConstructor", + "shortDescription": { + "text": "Abstract method called during object construction" + }, + "fullDescription": { + "text": "Reports calls to 'abstract' methods of the current class during object construction. A method is called during object construction if it is inside a: Constructor Non-static instance initializer Non-static field initializer 'clone()' method 'readObject()' method 'readObjectNoData()' method Such calls may result in subtle bugs, as object initialization may happen before the method call. Example: 'abstract class Parent {\n abstract void abstractMethod();\n }\n\n class Child extends Parent {\n Child() {\n abstractMethod();\n }\n }' This inspection shares the functionality with the following inspections: Overridable method called during object construction Overridden method called during object construction Only one inspection should be enabled at once to prevent warning duplication.", + "markdown": "Reports calls to `abstract` methods of the current class during object construction.\n\nA method is called during object construction if it is inside a:\n\n* Constructor\n* Non-static instance initializer\n* Non-static field initializer\n* `clone()` method\n* `readObject()` method\n* `readObjectNoData()` method\n\nSuch calls may result in subtle bugs, as object initialization may happen before the method call.\n\n**Example:**\n\n\n abstract class Parent {\n abstract void abstractMethod();\n }\n\n class Child extends Parent {\n Child() {\n abstractMethod();\n }\n }\n\nThis inspection shares the functionality with the following inspections:\n\n* Overridable method called during object construction\n* Overridden method called during object construction\n\nOnly one inspection should be enabled at once to prevent warning duplication." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AbstractMethodCallInConstructor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Initialization", + "index": 28, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AnonymousClassVariableHidesContainingMethodVariable", + "shortDescription": { + "text": "Anonymous class variable hides variable in containing method" + }, + "fullDescription": { + "text": "Reports fields in an anonymous class that are named identically to local variables or parameters of the containing method or lambda expression. As a result of such naming, you may accidentally use the anonymous class field where the identically named variable or parameter from the containing method is intended. A quick-fix is suggested to rename the field. Example: 'class Test {\n public Test(String value) {\n Object foo = new Object() {\n private String value = \"TEST\";\n public void foo() {\n System.out.println(value); //the field is accessed, not the parameter\n }\n };\n }\n }'", + "markdown": "Reports fields in an anonymous class that are named identically to local variables or parameters of the containing method or lambda expression.\n\n\nAs a result of such naming, you may accidentally use the anonymous class field where\nthe identically named variable or parameter from the containing method is intended.\n\nA quick-fix is suggested to rename the field.\n\n**Example:**\n\n\n class Test {\n public Test(String value) {\n Object foo = new Object() {\n private String value = \"TEST\";\n public void foo() {\n System.out.println(value); //the field is accessed, not the parameter\n }\n };\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AnonymousClassVariableHidesContainingMethodVariable", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Visibility", + "index": 57, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TailRecursion", + "shortDescription": { + "text": "Tail recursion" + }, + "fullDescription": { + "text": "Reports tail recursion, that is, when a method calls itself as its last action before returning. Tail recursion can always be replaced by looping, which will be considerably faster. Some JVMs perform tail-call optimization, while others do not. Thus, tail-recursive solutions may have considerably different performance characteristics on different virtual machines. Example: 'int factorial(int val, int runningVal) {\n if (val == 1) {\n return runningVal;\n } else {\n return factorial(val - 1, runningVal * val);\n }\n }' After the quick-fix is applied: 'int factorial(int val, int runningVal) {\n while (true) {\n if (val == 1) {\n return runningVal;\n } else {\n runningVal = runningVal * val;\n val = val - 1;\n }\n }\n }'", + "markdown": "Reports tail recursion, that is, when a method calls itself as its last action before returning.\n\n\nTail recursion can always be replaced by looping, which will be considerably faster.\nSome JVMs perform tail-call optimization, while others do not. Thus, tail-recursive solutions may have considerably different\nperformance characteristics on different virtual machines.\n\nExample:\n\n\n int factorial(int val, int runningVal) {\n if (val == 1) {\n return runningVal;\n } else {\n return factorial(val - 1, runningVal * val);\n }\n }\n\nAfter the quick-fix is applied:\n\n\n int factorial(int val, int runningVal) {\n while (true) {\n if (val == 1) {\n return runningVal;\n } else {\n runningVal = runningVal * val;\n val = val - 1;\n }\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "TailRecursion", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MethodRefCanBeReplacedWithLambda", + "shortDescription": { + "text": "Method reference can be replaced with lambda" + }, + "fullDescription": { + "text": "Reports method references, like 'MyClass::myMethod' and 'myObject::myMethod', and suggests replacing them with an equivalent lambda expression. Lambda expressions can be easier to modify than method references. Example: 'System.out::println' After the quick-fix is applied: 's -> System.out.println(s)' By default, this inspection does not highlight the code in the editor, but only provides a quick-fix.", + "markdown": "Reports method references, like `MyClass::myMethod` and `myObject::myMethod`, and suggests replacing them with an equivalent lambda expression.\n\nLambda expressions can be easier to modify than method references.\n\nExample:\n\n\n System.out::println\n\nAfter the quick-fix is applied:\n\n\n s -> System.out.println(s)\n\nBy default, this inspection does not highlight the code in the editor, but only provides a quick-fix." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "MethodRefCanBeReplacedWithLambda", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NestedSynchronizedStatement", + "shortDescription": { + "text": "Nested 'synchronized' statement" + }, + "fullDescription": { + "text": "Reports nested 'synchronized' statements. It is recommended to avoid nested synchronization if possible, because in some cases it may lead to a deadlock. Example: 'synchronized (lockA){\n //thread 1 is waiting for lockB\n synchronized (lockB){ //warning\n }\n }\n ...\n synchronized (lockB) {\n //thread 2 is waiting for lockA\n synchronized (lockA) { //warning\n }\n }'", + "markdown": "Reports nested `synchronized` statements. It is recommended to avoid nested synchronization if possible, because in some cases it may lead to a deadlock.\n\n**Example:**\n\n\n synchronized (lockA){\n //thread 1 is waiting for lockB\n synchronized (lockB){ //warning\n }\n }\n ...\n synchronized (lockB) {\n //thread 2 is waiting for lockA\n synchronized (lockA) { //warning\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NestedSynchronizedStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringEqualsEmptyString", + "shortDescription": { + "text": "'String.equals()' can be replaced with 'String.isEmpty()'" + }, + "fullDescription": { + "text": "Reports 'equals()' being called to compare a 'String' with an empty string. In this case, using '.isEmpty()' is better as it shows you exactly what you're checking. Example: 'void checkString(String s){\n if (\"\".equals(s)) throw new IllegalArgumentException();\n }' After the quick-fix is applied: 'void checkString(String s){\n if (s != null && s.isEmpty()) throw new IllegalArgumentException();\n }' '\"\".equals(str)' returns false when 'str' is null. For safety, this inspection's quick-fix inserts an explicit null-check when the 'equals()' argument is nullable. Use the option to make the inspection ignore such cases.", + "markdown": "Reports `equals()` being called to compare a `String` with an empty string. In this case, using `.isEmpty()` is better as it shows you exactly what you're checking.\n\n**Example:**\n\n\n void checkString(String s){\n if (\"\".equals(s)) throw new IllegalArgumentException();\n }\n\nAfter the quick-fix is applied:\n\n\n void checkString(String s){\n if (s != null && s.isEmpty()) throw new IllegalArgumentException();\n }\n\n\n`\"\".equals(str)` returns false when `str` is null. For safety, this inspection's quick-fix inserts an explicit\nnull-check when\nthe `equals()` argument is nullable. Use the option to make the inspection ignore such cases." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StringEqualsEmptyString", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IncorrectDateTimeFormat", + "shortDescription": { + "text": "Incorrect 'DateTimeFormat' pattern" + }, + "fullDescription": { + "text": "Reports incorrect date time format patterns. The following errors are reported: Unsupported pattern letters, like \"TT\" Using reserved characters, like \"#\" Incorrect use of padding Unbalanced brackets Incorrect amount of consecutive pattern letters Examples: 'DateTimeFormatter.ofPattern(\"[][]]\"); // Closing ']' without previous opening '['\n DateTimeFormatter.ofPattern(\"TT\"); // Illegal pattern letter 'T'\n DateTimeFormatter.ofPattern(\"{\"); // Use of reserved character '{'\n DateTimeFormatter.ofPattern(\"MMMMMM\"); // Too many consecutive pattern letters 'M'' New in 2022.3", + "markdown": "Reports incorrect date time format patterns.\n\nThe following errors are reported:\n\n* Unsupported pattern letters, like \"TT\"\n* Using reserved characters, like \"#\"\n* Incorrect use of padding\n* Unbalanced brackets\n* Incorrect amount of consecutive pattern letters\n\nExamples:\n\n\n DateTimeFormatter.ofPattern(\"[][]]\"); // Closing ']' without previous opening '['\n DateTimeFormatter.ofPattern(\"TT\"); // Illegal pattern letter 'T'\n DateTimeFormatter.ofPattern(\"{\"); // Use of reserved character '{'\n DateTimeFormatter.ofPattern(\"MMMMMM\"); // Too many consecutive pattern letters 'M'\n\nNew in 2022.3" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "IncorrectDateTimeFormat", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PreviewFeature", + "shortDescription": { + "text": "Preview Feature warning" + }, + "fullDescription": { + "text": "Reports usages of Preview Feature APIs, i.e. of a module, package, class, interface, method, constructor, field, or enum constant in the 'java.*' or 'javax.*' namespace annotated with '@PreviewFeature'. A preview feature is a new feature of the Java language, Java Virtual Machine, or Java SE API that is fully specified, fully implemented, and is yet impermanent. The notion of a preview feature is defined in JEP 12. If some piece of code depends on a preview API, it may stop compiling in future JDK versions if the feature is changed or removed. The inspection only reports if the language level of the project or module is Preview. New in 2021.1", + "markdown": "Reports usages of Preview Feature APIs, i.e. of a module, package, class, interface, method, constructor, field, or enum constant in the `java.*` or `javax.*` namespace annotated with `@PreviewFeature`.\n\n\nA preview feature is a new feature of the Java language, Java Virtual Machine, or Java SE API that is fully specified, fully implemented,\nand is yet impermanent. The notion of a preview feature is defined in [JEP 12](https://openjdk.org/jeps/12).\n\n\nIf some piece of code depends on a preview API, it may stop compiling in future JDK versions if the feature is changed or removed.\n\nThe inspection only reports if the language level of the project or module is **Preview**.\n\nNew in 2021.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "preview", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Compiler issues", + "index": 90, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TooBroadThrows", + "shortDescription": { + "text": "Overly broad 'throws' clause" + }, + "fullDescription": { + "text": "Reports 'throws' clauses with exceptions that are more generic than the exceptions that the method actually throws. Example: 'public void createFile() throws Exception { // warning: 'throws Exception' is too broad, masking exception 'IOException'\n File file = new File(\"pathToFile\");\n file.createNewFile();\n }' After the quick-fix is applied: 'public void createFile() throws IOException {\n File file = new File(\"pathToFile\");\n file.createNewFile();\n }' Configure the inspection: Use the Maximum number of hidden exceptions to warn field to ignore exceptions, that hide a larger number of other exceptions than specified. Use the Only warn on RuntimeException, Exception, Error or Throwable option to have this inspection warn only on the most generic exceptions. Use the Ignore exceptions declared on methods overriding a library method option to ignore overly broad 'throws' clauses in methods that override a library method. Use the Ignore exceptions which hide others but are themselves thrown option to ignore any exceptions that hide other exceptions but still may be thrown from the method body and thus are technically not overly broad.", + "markdown": "Reports `throws` clauses with exceptions that are more generic than the exceptions that the method actually throws.\n\n**Example:**\n\n\n public void createFile() throws Exception { // warning: 'throws Exception' is too broad, masking exception 'IOException'\n File file = new File(\"pathToFile\");\n file.createNewFile();\n }\n\nAfter the quick-fix is applied:\n\n\n public void createFile() throws IOException {\n File file = new File(\"pathToFile\");\n file.createNewFile();\n }\n\nConfigure the inspection:\n\n* Use the **Maximum number of hidden exceptions to warn** field to ignore exceptions, that hide a larger number of other exceptions than specified.\n* Use the **Only warn on RuntimeException, Exception, Error or Throwable** option to have this inspection warn only on the most generic exceptions.\n* Use the **Ignore exceptions declared on methods overriding a library method** option to ignore overly broad `throws` clauses in methods that override a library method.\n* Use the **Ignore exceptions which hide others but are themselves thrown** option to ignore any exceptions that hide other exceptions but still may be thrown from the method body and thus are technically not overly broad." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OverlyBroadThrowsClause", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ImplicitSubclassInspection", + "shortDescription": { + "text": "Final declaration can't be overridden at runtime" + }, + "fullDescription": { + "text": "Reports cases when your code prevents a class from being subclassed by some framework (for example, Spring or Hibernate) at runtime. Typical examples of necessary but impossible subclassing: 'final' classes marked with framework-specific annotations (for example, Spring '@Configuration') 'final', 'static' or 'private' methods marked with framework-specific annotations (for example, Spring '@Transactional') methods marked with framework-specific annotations inside 'final' classes The list of reported cases depends on the frameworks used.", + "markdown": "Reports cases when your code prevents a class from being subclassed by some framework (for example, Spring or Hibernate) at runtime.\n\nTypical examples of necessary but impossible subclassing:\n\n* `final` classes marked with framework-specific annotations (for example, Spring `@Configuration`)\n* `final`, `static` or `private` methods marked with framework-specific annotations (for example, Spring `@Transactional`)\n* methods marked with framework-specific annotations inside `final` classes\n\nThe list of reported cases depends on the frameworks used." + }, + "defaultConfiguration": { + "enabled": true, + "level": "error", + "parameters": { + "suppressToolId": "ImplicitSubclassInspection", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Inheritance issues", + "index": 25, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryFullyQualifiedName", + "shortDescription": { + "text": "Unnecessary fully qualified name" + }, + "fullDescription": { + "text": "Reports fully qualified class names that can be shortened. The quick-fix shortens fully qualified names and adds import statements if necessary. Example: 'class ListWrapper {\n java.util.List l;\n }' After the quick-fix is applied: 'import java.util.List;\n class ListWrapper {\n List l;\n }' Configure the inspection: Use the Ignore in Java 9 module statements option to ignore fully qualified names inside the Java 9 'provides' and 'uses' module statements. In Settings | Editor | Code Style | Java | Imports, use the following options to configure the inspection: Use the Insert imports for inner classes option if references to inner classes should be qualified with the outer class. Use the Use fully qualified class names in JavaDoc option to allow fully qualified names in Javadocs.", + "markdown": "Reports fully qualified class names that can be shortened.\n\nThe quick-fix shortens fully qualified names and adds import statements if necessary.\n\nExample:\n\n\n class ListWrapper {\n java.util.List l;\n }\n\nAfter the quick-fix is applied:\n\n\n import java.util.List;\n class ListWrapper {\n List l;\n }\n\nConfigure the inspection:\n\n\nUse the **Ignore in Java 9 module statements** option to ignore fully qualified names inside the Java 9\n`provides` and `uses` module statements.\n\n\nIn [Settings \\| Editor \\| Code Style \\| Java \\| Imports](settings://preferences.sourceCode.Java?JavaDoc%20Inner),\nuse the following options to configure the inspection:\n\n* Use the **Insert imports for inner classes** option if references to inner classes should be qualified with the outer class.\n* Use the **Use fully qualified class names in JavaDoc** option to allow fully qualified names in Javadocs." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "UnnecessaryFullyQualifiedName", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NegatedConditional", + "shortDescription": { + "text": "Conditional expression with negated condition" + }, + "fullDescription": { + "text": "Reports conditional expressions whose conditions are negated. Flipping the order of the conditional expression branches usually increases the clarity of such statements. Use the Ignore '!= null' comparisons and Ignore '!= 0' comparisons options to ignore comparisons of the form 'obj != null' or 'num != 0'. Since 'obj != null' effectively means \"obj exists\", the meaning of the whole expression does not involve any negation and is therefore easy to understand. The same reasoning applies to 'num != 0' expressions, especially when using bit masks. These forms have the added benefit of mentioning the interesting case first. In most cases, the value for the '== null' branch is 'null' itself, like in the following examples: 'static String getName(Person p) {\n return p != null ? p.getName() : null;\n }\n\n static String getExecutableString(int fileMode) {\n return (fileMode & 0b001001001) != 0 ? \"executable\" : \"non-executable\";\n }'", + "markdown": "Reports conditional expressions whose conditions are negated.\n\nFlipping the order of the conditional expression branches usually increases the clarity of such statements.\n\n\nUse the **Ignore '!= null' comparisons** and **Ignore '!= 0' comparisons** options to ignore comparisons of the form\n`obj != null` or `num != 0`.\nSince `obj != null` effectively means \"obj exists\",\nthe meaning of the whole expression does not involve any negation\nand is therefore easy to understand.\n\n\nThe same reasoning applies to `num != 0` expressions, especially when using bit masks.\n\n\nThese forms have the added benefit of mentioning the interesting case first.\nIn most cases, the value for the `== null` branch is `null` itself,\nlike in the following examples:\n\n\n static String getName(Person p) {\n return p != null ? p.getName() : null;\n }\n\n static String getExecutableString(int fileMode) {\n return (fileMode & 0b001001001) != 0 ? \"executable\" : \"non-executable\";\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ConditionalExpressionWithNegatedCondition", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ObjectEqualsCanBeEquality", + "shortDescription": { + "text": "'equals()' call can be replaced with '=='" + }, + "fullDescription": { + "text": "Reports calls to 'equals()' that can be replaced by '==' or '!=' expressions without a change in semantics. These calls can be replaced when they are used to compare 'final' classes that don't have their own 'equals()' implementation but use the default 'Object.equals()'. This replacement may result in better performance. There is a separate inspection for 'equals()' calls on 'enum' values: 'equals()' called on Enum value.", + "markdown": "Reports calls to `equals()` that can be replaced by `==` or `!=` expressions without a change in semantics.\n\nThese calls can be replaced when they are used to compare `final` classes that don't have their own `equals()` implementation but use the default `Object.equals()`.\nThis replacement may result in better performance.\n\nThere is a separate inspection for `equals()` calls on `enum` values: 'equals()' called on Enum value." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ObjectEqualsCanBeEquality", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BooleanParameter", + "shortDescription": { + "text": "'public' method with 'boolean' parameter" + }, + "fullDescription": { + "text": "Reports public methods that accept a 'boolean' parameter. It's almost always bad practice to add a 'boolean' parameter to a public method (part of an API) if that method is not a setter. When reading code using such a method, it can be difficult to decipher what the 'boolean' stands for without looking at the source or documentation. This problem is also known as the boolean trap. The 'boolean' parameter can often be replaced with an 'enum'. Example: '// Warning: it's hard to understand what the\n // boolean parameters mean when looking at\n // a call to this method\n public boolean setPermission(File f,\n int access,\n boolean enable,\n boolean ownerOnly) {\n // ...\n }' Use the Only report methods with multiple boolean parameters option to warn only when a method contains more than one boolean parameter.", + "markdown": "Reports public methods that accept a `boolean` parameter.\n\nIt's almost always bad practice to add a `boolean` parameter to a public method (part of an API) if that method is not a setter.\nWhen reading code using such a method, it can be difficult to decipher what the `boolean` stands for without looking at\nthe source or documentation.\n\nThis problem is also known as [the boolean trap](https://ariya.io/2011/08/hall-of-api-shame-boolean-trap).\nThe `boolean` parameter can often be replaced with an `enum`.\n\nExample:\n\n\n // Warning: it's hard to understand what the\n // boolean parameters mean when looking at\n // a call to this method\n public boolean setPermission(File f,\n int access,\n boolean enable,\n boolean ownerOnly) {\n // ...\n }\n\n\nUse the **Only report methods with multiple boolean parameters** option to warn only when a method contains more than one boolean parameter." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "BooleanParameter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Abstraction issues", + "index": 78, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JDBCPrepareStatementWithNonConstantString", + "shortDescription": { + "text": "Call to 'Connection.prepare*()' with non-constant string" + }, + "fullDescription": { + "text": "Reports calls to 'java.sql.Connection.prepareStatement()', 'java.sql.Connection.prepareCall()', or any of their variants which take a dynamically-constructed string as the statement to prepare. Constructed SQL statements are a common source of security breaches. By default, this inspection ignores compile-time constants. Example: 'String bar() { return \"bar\"; }\n\n Connection connection = DriverManager.getConnection(\"\", \"\", \"\");\n connection.(\"SELECT * FROM user WHERE name='\" + bar() + \"'\");' Use the inspection settings to consider any 'static' 'final' fields as constants. Be careful, because strings like the following will be ignored when the option is enabled: 'static final String SQL = \"SELECT * FROM user WHERE name='\" + getUserInput() + \"'\";'", + "markdown": "Reports calls to `java.sql.Connection.prepareStatement()`, `java.sql.Connection.prepareCall()`, or any of their variants which take a dynamically-constructed string as the statement to prepare.\n\n\nConstructed SQL statements are a common source of\nsecurity breaches. By default, this inspection ignores compile-time constants.\n\n**Example:**\n\n\n String bar() { return \"bar\"; }\n\n Connection connection = DriverManager.getConnection(\"\", \"\", \"\");\n connection.(\"SELECT * FROM user WHERE name='\" + bar() + \"'\");\n\nUse the inspection settings to consider any `static` `final` fields as constants. Be careful, because strings like the following will be ignored when the option is enabled:\n\n\n static final String SQL = \"SELECT * FROM user WHERE name='\" + getUserInput() + \"'\";\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "JDBCPrepareStatementWithNonConstantString", + "cweIds": [ + 89 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Security", + "index": 30, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SystemProperties", + "shortDescription": { + "text": "Access of system properties" + }, + "fullDescription": { + "text": "Reports code that accesses system properties using one of the following methods: 'System.getProperties()', 'System.setProperty()', 'System.setProperties()', 'System.clearProperties()' 'Integer.getInteger()' 'Boolean.getBoolean()' While accessing the system properties is not a security risk in itself, it is often found in malicious code. Code that accesses system properties should be closely examined in any security audit.", + "markdown": "Reports code that accesses system properties using one of the following methods:\n\n* `System.getProperties()`, `System.setProperty()`, `System.setProperties()`, `System.clearProperties()`\n* `Integer.getInteger()`\n* `Boolean.getBoolean()`\n\n\nWhile accessing the system properties is not a security risk in itself, it is often found in malicious code.\nCode that accesses system properties should be closely examined in any security audit." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AccessOfSystemProperties", + "cweIds": [ + 250, + 668 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Security", + "index": 30, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TestInProductSource", + "shortDescription": { + "text": "Test in product source" + }, + "fullDescription": { + "text": "Reports test classes and test methods that are located in production source trees. This most likely a mistake and can result in test code being shipped into production.", + "markdown": "Reports test classes and test methods that are located in production source trees. This most likely a mistake and can result in test code being shipped into production." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "TestInProductSource", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages/Test frameworks", + "index": 94, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CopyConstructorMissesField", + "shortDescription": { + "text": "Copy constructor misses field" + }, + "fullDescription": { + "text": "Reports copy constructors that don't copy all the fields of the class. 'final' fields with initializers and 'transient' fields are considered unnecessary to copy. Example: 'class Point {\n\n private int x;\n private int y;\n\n Point(int x, int y) {\n this.x = x;\n this.y = y;\n }\n\n Point(Point other) {\n // fields x and y are not initialized\n }\n }' New in 2018.1", + "markdown": "Reports copy constructors that don't copy all the fields of the class.\n\n\n`final` fields with initializers and `transient` fields are considered unnecessary to copy.\n\n**Example:**\n\n\n class Point {\n\n private int x;\n private int y;\n\n Point(int x, int y) {\n this.x = x;\n this.y = y;\n }\n\n Point(Point other) {\n // fields x and y are not initialized\n }\n }\n\nNew in 2018.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "CopyConstructorMissesField", + "cweIds": [ + 665 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InvalidComparatorMethodReference", + "shortDescription": { + "text": "Invalid method reference used for 'Comparator'" + }, + "fullDescription": { + "text": "Reports method references mapped to the 'Comparator' interface that don't fulfill its contract. Some method references, like 'Integer::max', can be mapped to the 'Comparator' interface. However, using them as 'Comparator' is meaningless and the result might be unpredictable. Example: 'ArrayList ints = foo();\n ints.sort(Math::min);' After the quick-fix is applied: 'ArrayList ints = foo();\n ints.sort(Comparator.reverseOrder());'", + "markdown": "Reports method references mapped to the `Comparator` interface that don't fulfill its contract.\n\n\nSome method references, like `Integer::max`, can be mapped to the `Comparator` interface.\nHowever, using them as `Comparator` is meaningless and the result might be unpredictable.\n\nExample:\n\n\n ArrayList ints = foo();\n ints.sort(Math::min);\n\nAfter the quick-fix is applied:\n\n\n ArrayList ints = foo();\n ints.sort(Comparator.reverseOrder());\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "InvalidComparatorMethodReference", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CastCanBeRemovedNarrowingVariableType", + "shortDescription": { + "text": "Too weak variable type leads to unnecessary cast" + }, + "fullDescription": { + "text": "Reports type casts that can be removed if the variable type is narrowed to the cast type. Example: 'Object x = \" string \";\n System.out.println(((String)x).trim());' Here, changing the type of 'x' to 'String' makes the cast redundant. The suggested quick-fix updates the variable type and removes all redundant casts on that variable: 'String x = \" string \";\n System.out.println(x.trim());' New in 2018.2", + "markdown": "Reports type casts that can be removed if the variable type is narrowed to the cast type.\n\nExample:\n\n\n Object x = \" string \";\n System.out.println(((String)x).trim());\n\n\nHere, changing the type of `x` to `String` makes the cast redundant. The suggested quick-fix updates the variable type and\nremoves all redundant casts on that variable:\n\n\n String x = \" string \";\n System.out.println(x.trim());\n\nNew in 2018.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CastCanBeRemovedNarrowingVariableType", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ToArrayCallWithZeroLengthArrayArgument", + "shortDescription": { + "text": "'Collection.toArray()' call style" + }, + "fullDescription": { + "text": "Reports 'Collection.toArray()' calls that are not in the preferred style, and suggests applying the preferred style. There are two styles to convert a collection to an array: A pre-sized array, for example, 'c.toArray(new String[c.size()])' An empty array, for example, 'c.toArray(new String[0])' In older Java versions, using a pre-sized array was recommended, as the reflection call necessary to create an array of proper size was quite slow. However, since late updates of OpenJDK 6, this call was intrinsified, making the performance of the empty array version the same, and sometimes even better, compared to the pre-sized version. Also, passing a pre-sized array is dangerous for a concurrent or synchronized collection as a data race is possible between the 'size' and 'toArray' calls. This may result in extra 'null's at the end of the array if the collection was concurrently shrunk during the operation. Use the inspection options to select the preferred style.", + "markdown": "Reports `Collection.toArray()` calls that are not in the preferred style, and suggests applying the preferred style.\n\nThere are two styles to convert a collection to an array:\n\n* A pre-sized array, for example, `c.toArray(new String[c.size()])`\n* An empty array, for example, `c.toArray(new String[0])`\n\nIn older Java versions, using a pre-sized array was recommended, as the reflection\ncall necessary to create an array of proper size was quite slow.\n\nHowever, since late updates of OpenJDK 6, this call was intrinsified, making\nthe performance of the empty array version the same, and sometimes even better, compared\nto the pre-sized version. Also, passing a pre-sized array is dangerous for a concurrent or\nsynchronized collection as a data race is possible between the `size` and `toArray`\ncalls. This may result in extra `null`s at the end of the array if the collection was concurrently\nshrunk during the operation.\n\nUse the inspection options to select the preferred style." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ToArrayCallWithZeroLengthArrayArgument", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java9ModuleExportsPackageToItself", + "shortDescription": { + "text": "Module exports/opens package to itself" + }, + "fullDescription": { + "text": "Reports packages that are exported to, or opened in the same Java 9 module in which they are defined. The quick-fix removes such directives from 'module-info.java'. Example: 'module com.mycomp {\n exports com.mycomp.main to com.mycomp;\n }' After the quick-fix is applied: 'module main {\n }' This inspection only reports if the language level of the project or module is 9 or higher.", + "markdown": "Reports packages that are exported to, or opened in the same Java 9 module in which they are defined. The quick-fix removes such directives from `module-info.java`.\n\nExample:\n\n\n module com.mycomp {\n exports com.mycomp.main to com.mycomp;\n }\n\nAfter the quick-fix is applied:\n\n\n module main {\n }\n\nThis inspection only reports if the language level of the project or module is 9 or higher." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "Java9ModuleExportsPackageToItself", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Visibility", + "index": 57, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LoggerInitializedWithForeignClass", + "shortDescription": { + "text": "Logger initialized with foreign class" + }, + "fullDescription": { + "text": "Reports 'Logger' instances that are initialized with a 'class' literal from a different class than the 'Logger' is contained in. This can easily happen when copy-pasting some code from another class and may result in logging events under an unexpected category and cause filters to be applied incorrectly. A quick-fix is provided to replace the foreign class literal with one from the surrounding class. Example: 'public class Paramount {\n protected static final Logger LOG = Logger.getLogger(Critical.class);\n\n // ... other fields and methods\n }' After the quick-fix is applied: 'public class Paramount {\n protected static final Logger LOG = Logger.getLogger(Paramount.class);\n\n // ... other fields and methods\n }' Configure the inspection: Use the table to specify the logger factory classes and logger factory methods recognized by this inspection. Use the Ignore loggers initialized with a superclass option to ignore loggers that are initialized with a superclass of the class containing the logger. Use the Ignore loggers in non-public classes to only warn on loggers in 'public' classes.", + "markdown": "Reports `Logger` instances that are initialized with a `class` literal from a different class than the `Logger` is contained in. This can easily happen when copy-pasting some code from another class and may result in logging events under an unexpected category and cause filters to be applied incorrectly.\n\nA quick-fix is provided to replace the foreign class literal with one from the surrounding class.\n\n**Example:**\n\n\n public class Paramount {\n protected static final Logger LOG = Logger.getLogger(Critical.class);\n\n // ... other fields and methods\n }\n\nAfter the quick-fix is applied:\n\n\n public class Paramount {\n protected static final Logger LOG = Logger.getLogger(Paramount.class);\n\n // ... other fields and methods\n }\n\n\nConfigure the inspection:\n\n* Use the table to specify the logger factory classes and logger factory methods recognized by this inspection.\n* Use the **Ignore loggers initialized with a superclass** option to ignore loggers that are initialized with a superclass of the class containing the logger.\n* Use the **Ignore loggers in non-public classes** to only warn on loggers in `public` classes." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LoggerInitializedWithForeignClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Logging", + "index": 68, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MarkerInterface", + "shortDescription": { + "text": "Marker interface" + }, + "fullDescription": { + "text": "Reports marker interfaces without any methods or fields. Such interfaces may be confusing and typically indicate a design failure. The inspection ignores interfaces that extend two or more interfaces and interfaces that specify the generic type of their superinterface.", + "markdown": "Reports marker interfaces without any methods or fields.\n\nSuch interfaces may be confusing and typically indicate a design failure.\n\nThe inspection ignores interfaces that extend two or more interfaces and interfaces\nthat specify the generic type of their superinterface." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MarkerInterface", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AssignmentToStaticFieldFromInstanceMethod", + "shortDescription": { + "text": "Assignment to static field from instance context" + }, + "fullDescription": { + "text": "Reports assignment to, or modification of 'static' fields from within an instance method. Although legal, such assignments are tricky to do safely and are often a result of marking fields 'static' inadvertently. Example: 'class Counter {\n private static int count = 0;\n\n void increment() {\n // Warning: updating a static field\n // from an instance method\n count++;\n }\n }'", + "markdown": "Reports assignment to, or modification of `static` fields from within an instance method.\n\nAlthough legal, such assignments are tricky to do\nsafely and are often a result of marking fields `static` inadvertently.\n\n**Example:**\n\n\n class Counter {\n private static int count = 0;\n\n void increment() {\n // Warning: updating a static field\n // from an instance method\n count++;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AssignmentToStaticFieldFromInstanceMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Assignment issues", + "index": 34, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CommentedOutCode", + "shortDescription": { + "text": "Commented out code" + }, + "fullDescription": { + "text": "Reports comments that contain Java code. Usually, code that is commented out gets outdated very quickly and becomes misleading. As most projects use some kind of version control system, it is better to delete commented out code completely and use the VCS history instead. New in 2020.3", + "markdown": "Reports comments that contain Java code.\n\nUsually, code that is commented out gets outdated very quickly and becomes misleading.\nAs most projects use some kind of version control system,\nit is better to delete commented out code completely and use the VCS history instead.\n\nNew in 2020.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "CommentedOutCode", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code maturity", + "index": 50, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SortedCollectionWithNonComparableKeys", + "shortDescription": { + "text": "Sorted collection with non-comparable elements" + }, + "fullDescription": { + "text": "Reports construction of sorted collections, for example 'TreeSet', that rely on natural ordering, whose element type doesn't implement the 'Comparable' interface. It's unlikely that such a collection will work properly. A false positive is possible if the collection element type is a non-comparable super-type, but the collection is intended to only hold comparable sub-types. Even if this is the case, it's better to narrow the collection element type or declare the super-type as 'Comparable' because the mentioned approach is error-prone. The inspection also reports cases when the collection element is a type parameter which is not declared as 'extends Comparable'. You can suppress the warnings on type parameters using the provided option (for example, to keep the API compatibility). New in 2018.3", + "markdown": "Reports construction of sorted collections, for example `TreeSet`, that rely on natural ordering, whose element type doesn't implement the `Comparable` interface.\n\nIt's unlikely that such a collection will work properly.\n\n\nA false positive is possible if the collection element type is a non-comparable super-type,\nbut the collection is intended to only hold comparable sub-types. Even if this is the case,\nit's better to narrow the collection element type or declare the super-type as `Comparable` because the mentioned approach is error-prone.\n\n\nThe inspection also reports cases when the collection element is a type parameter which is not declared as `extends Comparable`.\nYou can suppress the warnings on type parameters using the provided option (for example, to keep the API compatibility).\n\n\nNew in 2018.3" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SortedCollectionWithNonComparableKeys", + "cweIds": [ + 697 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassWithoutLogger", + "shortDescription": { + "text": "Class without logger" + }, + "fullDescription": { + "text": "Reports classes which do not have a declared logger. Ensuring that every class has a dedicated logger is an important step in providing a unified logging implementation for an application. Interfaces, enumerations, annotations, inner classes, and abstract classes are not reported by this inspection. For example: 'public class NoLoggerDeclared {\n\n int calculateNthDigitOfPi(int n) {\n // todo\n return 1;\n }\n }' Use the table in the Options section to specify logger class names. Classes which do not declare a field with the type of one of the specified classes will be reported by this inspection.", + "markdown": "Reports classes which do not have a declared logger.\n\nEnsuring that every class has a dedicated logger is an important step in providing a unified logging\nimplementation for an application. Interfaces, enumerations, annotations, inner classes, and abstract classes are not reported by this inspection.\n\nFor example:\n\n\n public class NoLoggerDeclared {\n\n int calculateNthDigitOfPi(int n) {\n // todo\n return 1;\n }\n }\n\n\nUse the table in the **Options** section to specify logger class names.\nClasses which do not declare a field with the type of one of the specified classes will be reported by this inspection." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassWithoutLogger", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Logging", + "index": 68, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReturnOfInnerClass", + "shortDescription": { + "text": "Return of instance of anonymous, local or inner class" + }, + "fullDescription": { + "text": "Reports 'return' statements that return an instance of an anonymous, local, or inner class. Such instances keep an implicit reference to the outer instance, which can prevent the outer instance from being garbage-collected. Any caller of a method returning such an instance might cause a memory leak by holding on to the instance returned. Configure the inspection: Use the Ignore returns from non-public methods option to ignore returns from 'protected' or package-private methods. Returns from 'private' methods are always ignored.", + "markdown": "Reports `return` statements that return an instance of an anonymous, local, or inner class. Such instances keep an implicit reference to the outer instance, which can prevent the outer instance from being garbage-collected. Any caller of a method returning such an instance might cause a memory leak by holding on to the instance returned.\n\n\nConfigure the inspection:\n\n* Use the **Ignore returns from non-public methods** option to ignore returns from `protected` or package-private methods. Returns from `private` methods are always ignored." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ReturnOfInnerClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Memory", + "index": 73, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AbstractClassWithOnlyOneDirectInheritor", + "shortDescription": { + "text": "Abstract class with a single direct inheritor" + }, + "fullDescription": { + "text": "Reports abstract classes that have precisely one direct inheritor. While such classes may offer admirable clarity of design, in memory-constrained or bandwidth-limited environments, they needlessly increase the total footprint of the application. Consider merging the abstract class with its inheritor. This inspection is intended for Java ME and other highly resource constrained environments. Applying the results of this inspection without consideration might have negative effects on code clarity and design. Example: 'abstract class Base {} // will be reported\n\n class Inheritor extends Base {}'", + "markdown": "Reports abstract classes that have precisely one direct inheritor. While such classes may offer admirable clarity of design, in memory-constrained or bandwidth-limited environments, they needlessly increase the total footprint of the application. Consider merging the abstract class with its inheritor.\n\n\nThis inspection is intended for Java ME and other highly resource constrained environments.\nApplying the results of this inspection without consideration might have negative effects on code clarity and design.\n\n**Example:**\n\n\n abstract class Base {} // will be reported\n\n class Inheritor extends Base {}\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AbstractClassWithOnlyOneDirectInheritor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance/Embedded", + "index": 19, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AnonymousClassComplexity", + "shortDescription": { + "text": "Overly complex anonymous class" + }, + "fullDescription": { + "text": "Reports anonymous inner classes whose total complexity exceeds the specified maximum. The total complexity of a class is the sum of cyclomatic complexities of all the methods and initializers the class declares. Inherited methods and initializers are not counted toward the total complexity. Anonymous classes should have very low complexity otherwise they are hard to understand and should be promoted to become named inner classes. Use the Cyclomatic complexity limit field to specify the maximum allowed complexity for a class.", + "markdown": "Reports anonymous inner classes whose total complexity exceeds the specified maximum.\n\nThe total complexity of a class is the sum of cyclomatic complexities of all the methods\nand initializers the class declares. Inherited methods and initializers are not counted\ntoward the total complexity.\n\nAnonymous classes should have very low complexity otherwise they are hard to understand and should be promoted to become named inner classes.\n\nUse the **Cyclomatic complexity limit** field to specify the maximum allowed complexity for a class." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OverlyComplexAnonymousInnerClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class metrics", + "index": 87, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "WaitWithoutCorrespondingNotify", + "shortDescription": { + "text": "'wait()' without corresponding 'notify()'" + }, + "fullDescription": { + "text": "Reports calls to 'Object.wait()', for which no call to the corresponding 'Object.notify()' or 'Object.notifyAll()' can be found. This inspection only reports calls with qualifiers referencing fields of the current class. Example: 'public class Foo {\n public Object foo = new Object();\n\n void bar() throws InterruptedException {\n this.foo.wait();\n }\n }'", + "markdown": "Reports calls to `Object.wait()`, for which no call to the corresponding `Object.notify()` or `Object.notifyAll()` can be found.\n\nThis inspection only reports calls with qualifiers referencing fields of the current class.\n\n**Example:**\n\n\n public class Foo {\n public Object foo = new Object();\n\n void bar() throws InterruptedException {\n this.foo.wait();\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "WaitWithoutCorrespondingNotify", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnsecureRandomNumberGeneration", + "shortDescription": { + "text": "Insecure random number generation" + }, + "fullDescription": { + "text": "Reports any uses of 'java.lang.Random' or 'java.lang.Math.random()'. In secure environments, 'java.secure.SecureRandom' is a better choice, since is offers cryptographically secure random number generation. Example: 'long token = new Random().nextLong();'", + "markdown": "Reports any uses of `java.lang.Random` or `java.lang.Math.random()`.\n\n\nIn secure environments,\n`java.secure.SecureRandom` is a better choice, since is offers cryptographically secure\nrandom number generation.\n\n**Example:**\n\n\n long token = new Random().nextLong();\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnsecureRandomNumberGeneration", + "cweIds": [ + 330 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Security", + "index": 30, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NullableProblems", + "shortDescription": { + "text": "@NotNull/@Nullable problems" + }, + "fullDescription": { + "text": "Reports problems related to nullability annotations. Examples: Overriding methods are not annotated: 'abstract class A {\n @NotNull abstract String m();\n}\nclass B extends A {\n String m() { return \"empty string\"; }\n}' Annotated primitive types: '@NotNull int myFoo;' Both '@Nullable' and '@NotNull' are present on the same member: '@Nullable @NotNull String myFooString;' Collection of nullable elements is assigned into a collection of non-null elements: 'void testList(List<@Nullable String> nullableList) {\n List<@NotNull String> list2 = nullableList;\n}' Use the Configure Annotations button to specify nullability annotations and the checkboxes to fine-tune where the inspection should provide warnings. This inspection only reports if the language level of the project or module is 5 or higher, and nullability annotations are available on the classpath.", + "markdown": "Reports problems related to nullability annotations.\n\n**Examples:**\n\n* Overriding methods are not annotated:\n\n\n abstract class A {\n @NotNull abstract String m();\n }\n class B extends A {\n String m() { return \"empty string\"; }\n }\n \n* Annotated primitive types: `@NotNull int myFoo;`\n* Both `@Nullable` and `@NotNull` are present on the same member: `@Nullable @NotNull String myFooString;`\n* Collection of nullable elements is assigned into a collection of non-null elements:\n\n\n void testList(List<@Nullable String> nullableList) {\n List<@NotNull String> list2 = nullableList;\n }\n \nUse the **Configure Annotations** button to specify nullability annotations and the checkboxes to fine-tune where the inspection should provide warnings.\n\nThis inspection only reports if the language level of the project or module is 5 or higher,\nand nullability annotations are available on the classpath." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NullableProblems", + "cweIds": [ + 476, + 754 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs/Nullability problems", + "index": 115, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UseOfAWTPeerClass", + "shortDescription": { + "text": "Use of AWT peer class" + }, + "fullDescription": { + "text": "Reports uses of AWT peer classes. Such classes represent native windowing system widgets, and will be non-portable between different windowing systems. Example: 'import java.awt.peer.ButtonPeer;\n\n abstract class Sample implements ButtonPeer {\n public void foo() {\n Sample sample;\n }\n }'", + "markdown": "Reports uses of AWT peer classes. Such classes represent native windowing system widgets, and will be non-portable between different windowing systems.\n\n**Example:**\n\n\n import java.awt.peer.ButtonPeer;\n\n abstract class Sample implements ButtonPeer {\n public void foo() {\n Sample sample;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UseOfAWTPeerClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Portability", + "index": 7, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EqualsBetweenInconvertibleTypes", + "shortDescription": { + "text": "'equals()' between objects of inconvertible types" + }, + "fullDescription": { + "text": "Reports calls to 'equals()' where the target and argument are of incompatible types. While such a call might theoretically be useful, most likely it is a bug. Example: 'new HashSet().equals(new TreeSet());'", + "markdown": "Reports calls to `equals()` where the target and argument are of incompatible types.\n\nWhile such a call might theoretically be useful, most likely it is a bug.\n\n**Example:**\n\n\n new HashSet().equals(new TreeSet());\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "EqualsBetweenInconvertibleTypes", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SynchronizationOnGetClass", + "shortDescription": { + "text": "Synchronization on 'getClass()'" + }, + "fullDescription": { + "text": "Reports synchronization on a call to 'getClass()'. If the class containing the synchronization is subclassed, the subclass will synchronize on a different class object. Usually the call to 'getClass()' can be replaced with a class literal expression, for example 'String.class'. An even better solution is synchronizing on a 'private static final' lock object, access to which can be completely controlled. Example: 'synchronized(getClass()) {}'", + "markdown": "Reports synchronization on a call to `getClass()`.\n\n\nIf the class containing the synchronization is subclassed, the subclass\nwill\nsynchronize on a different class object. Usually the call to `getClass()` can be replaced with a class literal expression, for\nexample `String.class`. An even better solution is synchronizing on a `private static final` lock object, access to\nwhich can be completely controlled.\n\n**Example:**\n\n synchronized(getClass()) {}\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SynchronizationOnGetClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DuplicateCondition", + "shortDescription": { + "text": "Duplicate condition" + }, + "fullDescription": { + "text": "Reports duplicate conditions in '&&' and '||' expressions and branches of 'if' statements. While sometimes duplicate conditions are intended, in most cases they the result of an oversight. Example: 'boolean result = digit1 != digit2 || digit1 != digit2;' To ignore conditions that may produce side effects, use the Ignore conditions with side effects option. Disabling this option may lead to false-positives, for example, when the same method returns different values on subsequent invocations. Example: 'if (iterator.next() != null || iterator.next() != null) {\n System.out.println(\"Got it\");\n }' Due to possible side effects of 'iterator.next()' (on the example), the warning will only be triggered if the Ignore conditions with side effects option is disabled.", + "markdown": "Reports duplicate conditions in `&&` and `||` expressions and branches of `if` statements. While sometimes duplicate conditions are intended, in most cases they the result of an oversight.\n\nExample:\n\n\n boolean result = digit1 != digit2 || digit1 != digit2;\n\n\nTo ignore conditions that may produce side effects, use the **Ignore conditions with side effects** option.\nDisabling this option may lead to false-positives, for example, when the same method returns different values on subsequent invocations.\n\nExample:\n\n\n if (iterator.next() != null || iterator.next() != null) {\n System.out.println(\"Got it\");\n }\n\nDue to possible side effects of `iterator.next()` (on the example), the warning will only be\ntriggered if the **Ignore conditions with side effects** option is disabled." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "DuplicateCondition", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantExplicitVariableType", + "shortDescription": { + "text": "Local variable type can be omitted" + }, + "fullDescription": { + "text": "Reports redundant local variable types. These types can be inferred from the context and thus replaced with 'var'. Example: 'void test(InputStream s) {\n try (InputStream in = s) {}\n }' After the fix is applied: 'void test(InputStream s) {\n try (var in = s) {}\n }'", + "markdown": "Reports redundant local variable types.\n\nThese types can be inferred from the context and thus replaced with `var`.\n\n**Example:**\n\n\n void test(InputStream s) {\n try (InputStream in = s) {}\n }\n\nAfter the fix is applied:\n\n\n void test(InputStream s) {\n try (var in = s) {}\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RedundantExplicitVariableType", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 10", + "index": 110, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ThrownExceptionsPerMethod", + "shortDescription": { + "text": "Method with too many exceptions declared" + }, + "fullDescription": { + "text": "Reports methods that have too many types of exceptions in its 'throws' list. Methods with too many exceptions declared are a good sign that your error handling code is getting overly complex. Use the Exceptions thrown limit field to specify the maximum number of exception types a method is allowed to have in its 'throws' list.", + "markdown": "Reports methods that have too many types of exceptions in its `throws` list.\n\nMethods with too many exceptions declared are a good sign that your error handling code is getting overly complex.\n\nUse the **Exceptions thrown limit** field to specify the maximum number of exception types a method is allowed to have in its `throws` list." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MethodWithTooExceptionsDeclared", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Method metrics", + "index": 95, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantThrows", + "shortDescription": { + "text": "Redundant 'throws' clause" + }, + "fullDescription": { + "text": "Reports exceptions that are declared in a method's signature but never thrown by the method itself or its implementations and overriding methods. The inspection ignores methods related to serialization, for example the methods 'readObject()' and 'writeObject()'. Example: 'void method() throws InterruptedException {\n System.out.println();\n }' The quick-fix removes unnecessary exceptions from the declaration and normalizes redundant 'try'-'catch' statements: 'void method() {\n System.out.println();\n }' Note: Some exceptions may not be reported during in-editor highlighting for performance reasons. To see all results, run the inspection by selecting Code | Inspect Code or Code | Analyze Code | Run Inspection by Name from the main menu. Use the Ignore exceptions thrown by entry point methods option to not report exceptions thrown by for example 'main()' methods. Entry point methods can be configured in the settings of the Java | Declaration redundancy | Unused declaration inspection.", + "markdown": "Reports exceptions that are declared in a method's signature but never thrown by the method itself or its implementations and overriding methods.\n\nThe inspection ignores methods related to serialization, for example the methods `readObject()` and `writeObject()`.\n\n**Example:**\n\n\n void method() throws InterruptedException {\n System.out.println();\n }\n\nThe quick-fix removes unnecessary exceptions from the declaration and normalizes redundant `try`-`catch` statements:\n\n\n void method() {\n System.out.println();\n }\n\n\n**Note:** Some exceptions may not be reported during in-editor highlighting for performance reasons.\nTo see all results, run the inspection by selecting **Code \\| Inspect Code** or **Code \\| Analyze Code \\| Run Inspection by Name** from the main menu.\n\nUse the **Ignore exceptions thrown by entry point methods** option to not report exceptions thrown by\nfor example `main()` methods.\nEntry point methods can be configured in the settings of the\n[Java \\| Declaration redundancy \\| Unused declaration](settings://Errors?Unused%20Declaration%20entry%20point) inspection." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantThrows", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SerializableWithUnconstructableAncestor", + "shortDescription": { + "text": "Serializable class with unconstructable ancestor" + }, + "fullDescription": { + "text": "Reports 'Serializable' classes whose closest non-serializable ancestor doesn't have a no-argument constructor. Such classes cannot be deserialized and will fail with an 'InvalidClassException'. Example: 'class Ancestor {\n private String name;\n Ancestor(String name) {\n this.name = name;\n }\n }\n\n // warning on this class because the superclass is not\n // serializable, and its constructor takes arguments\n class Descendant extends Ancestor implements Serializable {\n Descendant() {\n super(\"Bob\");\n }\n }'", + "markdown": "Reports `Serializable` classes whose closest non-serializable ancestor doesn't have a no-argument constructor. Such classes cannot be deserialized and will fail with an `InvalidClassException`.\n\n**Example:**\n\n\n class Ancestor {\n private String name;\n Ancestor(String name) {\n this.name = name;\n }\n }\n\n // warning on this class because the superclass is not\n // serializable, and its constructor takes arguments\n class Descendant extends Ancestor implements Serializable {\n Descendant() {\n super(\"Bob\");\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SerializableClassWithUnconstructableAncestor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ExcessiveLambdaUsage", + "shortDescription": { + "text": "Excessive lambda usage" + }, + "fullDescription": { + "text": "Reports if a trivial lambda expression is used in cases in which there's an alternative method that behaves in the same way, but accepts a concrete value instead of a lambda. This inspection helps simplify the code. Example: 'Optional.orElseGet(() -> null)' After the quick-fix is applied: 'Optional.orElse(null)' New in 2017.1", + "markdown": "Reports if a trivial lambda expression is used in cases in which there's an alternative method that behaves in the same way, but accepts a concrete value instead of a lambda.\n\nThis inspection helps simplify the code.\n\nExample:\n\n\n Optional.orElseGet(() -> null)\n\nAfter the quick-fix is applied:\n\n\n Optional.orElse(null)\n\nNew in 2017.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ExcessiveLambdaUsage", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ParameterHidingMemberVariable", + "shortDescription": { + "text": "Parameter hides field" + }, + "fullDescription": { + "text": "Reports method parameters named identically to a field of a surrounding class. As a result of such naming, you may accidentally use the parameter when using the identically named field is intended. A quick-fix is suggested to rename the parameter. Example: 'class Main {\n private String value;\n\n public Main(String value) {\n value = value.toUpperCase();\n }\n }' You can configure the following options for this inspection: Ignore for property setters - ignore parameters of simple setters. Ignore superclass fields not visible from subclass - ignore 'private' fields in a superclass, which are not visible from the method. Ignore for constructors - ignore parameters of constructors. Ignore for abstract methods - ignore parameters of abstract methods. Ignore for static method parameters hiding instance fields - ignore parameters of 'static' methods hiding an instance field and to ignore parameters of instance methods in static inner classes hiding an instance field of an outer class. While not strictly hiding, such parameters can still be confusing.", + "markdown": "Reports method parameters named identically to a field of a surrounding class. As a result of such naming, you may accidentally use the parameter when using the identically named field is intended.\n\nA quick-fix is suggested to rename the parameter.\n\n**Example:**\n\n\n class Main {\n private String value;\n\n public Main(String value) {\n value = value.toUpperCase();\n }\n }\n \n\nYou can configure the following options for this inspection:\n\n1. **Ignore for property setters** - ignore parameters of simple setters.\n2. **Ignore superclass fields not visible from subclass** - ignore `private` fields in a superclass, which are not visible from the method.\n3. **Ignore for constructors** - ignore parameters of constructors.\n4. **Ignore for abstract methods** - ignore parameters of abstract methods.\n5. **Ignore for static method parameters hiding instance fields** - ignore parameters of `static` methods hiding an instance field and to ignore parameters of instance methods in static inner classes hiding an instance field of an outer class. While not strictly hiding, such parameters can still be confusing." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ParameterHidesMemberVariable", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Visibility", + "index": 57, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LambdaBodyCanBeCodeBlock", + "shortDescription": { + "text": "Lambda body can be code block" + }, + "fullDescription": { + "text": "Reports lambdas whose body is an expression and suggests converting expression bodies to code blocks. Example: 'n -> n + 1' After the quick-fix is applied: 'n -> {\n return n + 1;\n}'", + "markdown": "Reports lambdas whose body is an expression and suggests converting expression bodies to code blocks.\n\nExample:\n\n\n n -> n + 1\n\nAfter the quick-fix is applied:\n\n n -> {\n return n + 1;\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "LambdaBodyCanBeCodeBlock", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UseOfAnotherObjectsPrivateField", + "shortDescription": { + "text": "Accessing a non-public field of another object" + }, + "fullDescription": { + "text": "Reports accesses to 'private' or 'protected' fields of another object. Java allows access to such fields for objects of the same class as the current object but some coding styles discourage this use. Additionally, such direct access to 'private' fields may fail in component-oriented architectures, such as Spring or Hibernate, that expect all access to other objects to be through method calls so the framework can mediate access using proxies. Example: 'public class Base {\n protected int bar;\n\n void increment(Base base) {\n bar++;\n base.bar++; // warning: direct access to another object's non-public field\n }\n }' A quick-fix to encapsulate the field is available. Configure the inspection: Use the Ignore accesses from the same class option to ignore access from the same class and only report access from inner or outer classes. To ignore access from inner classes as well, use the nested Ignore accesses from inner classes. Use the Ignore accesses from 'equals()' method to ignore access from an 'equals()' method.", + "markdown": "Reports accesses to `private` or `protected` fields of another object. Java allows access to such fields for objects of the same class as the current object but some coding styles discourage this use. Additionally, such direct access to `private` fields may fail in component-oriented architectures, such as Spring or Hibernate, that expect all access to other objects to be through method calls so the framework can mediate access using proxies.\n\n**Example:**\n\n\n public class Base {\n protected int bar;\n\n void increment(Base base) {\n bar++;\n base.bar++; // warning: direct access to another object's non-public field\n }\n }\n\nA quick-fix to encapsulate the field is available.\n\nConfigure the inspection:\n\n* Use the **Ignore accesses from the same class** option to ignore access from the same class and only report access from inner or outer classes.\n\n To ignore access from inner classes as well, use the nested **Ignore accesses from inner classes**.\n* Use the **Ignore accesses from 'equals()' method** to ignore access from an `equals()` method." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AccessingNonPublicFieldOfAnotherObject", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Encapsulation", + "index": 60, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PointlessBitwiseExpression", + "shortDescription": { + "text": "Pointless bitwise expression" + }, + "fullDescription": { + "text": "Reports pointless bitwise expressions. Such expressions include applying the '&' operator to the maximum value for the given type, applying the 'or' operator to zero, and shifting by zero. Such expressions may be the result of automated refactorings not followed through to completion and are unlikely to be originally intended. Examples: '// Warning: operation is pointless and can be replaced with just `flags`\n // 0xFFFF_FFFF is the maximum value for an integer, and both literals are treated\n // as 32 bit integer literals.\n int bits = flags & 0xFFFF_FFFF;\n\n // Warning: operation is pointless and can be replaced with just `bits`\n // OR-ing with 0 always outputs the other operand.\n int or = bits | 0x0;\n\n // Warning: operation is pointless, as always results in 0\n int xor = or ^ or;'", + "markdown": "Reports pointless bitwise expressions.\n\n\nSuch expressions include applying the `&` operator to the maximum value for the given type, applying the\n`or` operator to zero, and shifting by zero. Such expressions may be the result of automated\nrefactorings not followed through to completion and are unlikely to be originally intended.\n\n**Examples:**\n\n\n // Warning: operation is pointless and can be replaced with just `flags`\n // 0xFFFF_FFFF is the maximum value for an integer, and both literals are treated\n // as 32 bit integer literals.\n int bits = flags & 0xFFFF_FFFF;\n\n // Warning: operation is pointless and can be replaced with just `bits`\n // OR-ing with 0 always outputs the other operand.\n int or = bits | 0x0;\n\n // Warning: operation is pointless, as always results in 0\n int xor = or ^ or;\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "PointlessBitwiseExpression", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Bitwise operation issues", + "index": 97, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CustomSecurityManager", + "shortDescription": { + "text": "Custom 'SecurityManager'" + }, + "fullDescription": { + "text": "Reports user-defined subclasses of 'java.lang.SecurityManager'. While not necessarily representing a security hole, such classes should be thoroughly and professionally inspected for possible security issues. Example: 'class CustomSecurityManager extends SecurityManager {\n }'", + "markdown": "Reports user-defined subclasses of `java.lang.SecurityManager`.\n\n\nWhile not necessarily representing a security hole, such classes should be thoroughly\nand professionally inspected for possible security issues.\n\n**Example:**\n\n\n class CustomSecurityManager extends SecurityManager {\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CustomSecurityManager", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Security", + "index": 30, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TimeToString", + "shortDescription": { + "text": "Call to 'Time.toString()'" + }, + "fullDescription": { + "text": "Reports 'toString()' calls on 'java.sql.Time' objects. Such calls are usually incorrect in an internationalized environment.", + "markdown": "Reports `toString()` calls on `java.sql.Time` objects. Such calls are usually incorrect in an internationalized environment." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToTimeToString", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Internationalization", + "index": 9, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ObjectEquality", + "shortDescription": { + "text": "Object comparison using '==', instead of 'equals()'" + }, + "fullDescription": { + "text": "Reports code that uses '==' or '!=' rather than 'equals()' to test for object equality. Comparing objects using '==' or '!=' is often a bug, because it compares objects by identity instead of equality. Comparisons to 'null' are not reported. Array, 'String' and 'Number' comparisons are reported by separate inspections. Example: 'if (list1 == list2) {\n return;\n }' After the quick-fix is applied: 'if (Objects.equals(list1, list2)) {\n return;\n }' Use the inspection settings to configure exceptions for this inspection.", + "markdown": "Reports code that uses `==` or `!=` rather than `equals()` to test for object equality.\n\n\nComparing objects using `==` or `!=` is often a bug,\nbecause it compares objects by identity instead of equality.\nComparisons to `null` are not reported.\n\n\nArray, `String` and `Number` comparisons are reported by separate inspections.\n\n**Example:**\n\n if (list1 == list2) {\n return;\n }\n\nAfter the quick-fix is applied:\n\n if (Objects.equals(list1, list2)) {\n return;\n }\n\nUse the inspection settings to configure exceptions for this inspection." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ObjectEquality", + "cweIds": [ + 480 + ], + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DuplicateThrows", + "shortDescription": { + "text": "Duplicate throws" + }, + "fullDescription": { + "text": "Reports duplicate exceptions in a method 'throws' list. Example: 'void f() throws Exception, Exception {}' After the quick-fix is applied: 'void f() throws Exception {}' Use the Ignore exceptions subclassing others option to ignore exceptions subclassing other exceptions.", + "markdown": "Reports duplicate exceptions in a method `throws` list.\n\nExample:\n\n\n void f() throws Exception, Exception {}\n\nAfter the quick-fix is applied:\n\n\n void f() throws Exception {}\n\n\nUse the **Ignore exceptions subclassing others** option to ignore exceptions subclassing other exceptions." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "DuplicateThrows", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnstableTypeUsedInSignature", + "shortDescription": { + "text": "Unstable type is used in signature" + }, + "fullDescription": { + "text": "Reports declarations of classes, methods, and fields that reference an unstable API type in the signature, but are not marked with the same unstable annotation. This inspection ensures that the signatures of a public API do not expose any unstable (internal, experimental) types. For example, if a method returns an experimental class, the method itself is considered experimental because incompatible changes of the type (deletion or move to another package) lead to incompatible method signature changes. Use the list below to specify which annotations mark an unstable API.", + "markdown": "Reports declarations of classes, methods, and fields that reference an unstable API type in the signature, but are not marked with the same unstable annotation.\n\n\nThis inspection ensures that the signatures of a public API do not expose any *unstable* (internal, experimental) types.\nFor example, if a method returns an *experimental* class, the method itself is considered *experimental*\nbecause incompatible changes of the type (deletion or move to another package) lead to incompatible method signature changes.\n\nUse the list below to specify which annotations mark an unstable API." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnstableTypeUsedInSignature", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages", + "index": 2, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PrivateMemberAccessBetweenOuterAndInnerClass", + "shortDescription": { + "text": "Synthetic accessor call" + }, + "fullDescription": { + "text": "Reports references from a nested class to non-constant 'private' members of an outer class. For such references, javac will generate package-private synthetic accessor methods, which may compromise the security because members appearing to be private will in fact be accessible from the entire package. A nested class and its outer class are compiled to separate class files. The Java virtual machine normally prohibits access from a class to private fields and methods of another class. To enable access from a nested class to private members of an outer class, javac creates a package-private synthetic accessor method. By making the 'private' member package-private instead, the actual accessibility is made explicit. This also saves a little bit of memory, which may improve performance in resource constrained environments. This inspection only reports if the language level of the project or module is 10 or lower. Under Java 11 and higher accessor methods are not generated anymore, because of nest-based access control (JEP 181). Example: 'class Outer {\n private void x() {}\n\n class Inner {\n void y() {\n x();\n }\n }\n }' After the quick fix is applied: 'class Outer {\n void x() {}\n\n class Inner {\n void y() {\n x();\n }\n }\n }'", + "markdown": "Reports references from a nested class to non-constant `private` members of an outer class. For such references, javac will generate package-private synthetic accessor methods, which may compromise the security because members appearing to be private will in fact be accessible from the entire package.\n\n\nA nested class and its outer class are compiled to separate\nclass files. The Java virtual machine normally prohibits access from a class to private fields and methods of\nanother class. To enable access from a nested class to private members of an outer class, javac creates a package-private\nsynthetic accessor method.\n\n\nBy making the `private` member package-private instead, the actual accessibility is made explicit.\nThis also saves a little bit of memory, which may improve performance in resource constrained environments.\n\n\nThis inspection only reports if the language level of the project or module is 10 or lower.\nUnder Java 11 and higher accessor methods are not generated anymore,\nbecause of nest-based access control ([JEP 181](https://openjdk.org/jeps/181)).\n\n**Example:**\n\n\n class Outer {\n private void x() {}\n\n class Inner {\n void y() {\n x();\n }\n }\n }\n\nAfter the quick fix is applied:\n\n\n class Outer {\n void x() {}\n\n class Inner {\n void y() {\n x();\n }\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SyntheticAccessorCall", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance/Embedded", + "index": 19, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SystemRunFinalizersOnExit", + "shortDescription": { + "text": "Call to 'System.runFinalizersOnExit()'" + }, + "fullDescription": { + "text": "Reports calls to 'System.runFinalizersOnExit()'. This call is one of the most dangerous in the Java language. It is inherently non-thread-safe, may result in data corruption, a deadlock, and may affect parts of the program far removed from its call point. It is deprecated and was removed in JDK 11, and its use is strongly discouraged. This inspection only reports if the language level of the project or module is 10 or lower.", + "markdown": "Reports calls to `System.runFinalizersOnExit()`.\n\n\nThis call is one of the most dangerous in the Java language. It is inherently non-thread-safe,\nmay result in data corruption, a deadlock, and may affect parts of the program far removed from its call point.\nIt is deprecated and was removed in JDK 11, and its use is strongly discouraged.\n\nThis inspection only reports if the language level of the project or module is 10 or lower." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "CallToSystemRunFinalizersOnExit", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassIndependentOfModule", + "shortDescription": { + "text": "Class independent of its module" + }, + "fullDescription": { + "text": "Reports classes that: do not depend on any other class in their module are not a dependency for any other class in their module Such classes are an indication of ad-hoc or incoherent modularisation strategies, and may often profitably be moved. Available only from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name and isn't reported in the editor.", + "markdown": "Reports classes that:\n\n* do not depend on any other class in their module\n* are not a dependency for any other class in their module\n\nSuch classes are an indication of ad-hoc or incoherent modularisation strategies,\nand may often profitably be moved.\n\nAvailable only from **Code \\| Inspect Code** or\n**Code \\| Analyze Code \\| Run Inspection by Name** and isn't reported in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassIndependentOfModule", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Modularization issues", + "index": 69, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PatternVariablesCanBeReplacedWithCast", + "shortDescription": { + "text": "Using 'instanceof' with patterns" + }, + "fullDescription": { + "text": "Reports 'instanceof' with patterns and suggests converting them to ordinary 'instanceof' with casts. This inspection makes it possible to move 'instanceof' with patterns to a codebase using an earlier Java version by applying the quick-fix. Note that the result can be not completely equivalent to the original 'instanceof' with patterns when a complex expression before 'instanceof' is used. In this case this expression will be reevaluated. Example: 'if (object instanceof String txt && txt.length() == 1) {\n System.out.println(txt);\n } else {\n return;\n }\n System.out.println(txt);' After the quick-fix is applied: 'if (object instanceof String && ((String) object).length() ==1) {\n String txt = (String) object;\n System.out.println(txt);\n } else {\n return;\n }\n String txt = (String) object;\n System.out.println(txt);' New in 2023.1", + "markdown": "Reports `instanceof` with patterns and suggests converting them to ordinary `instanceof` with casts.\n\nThis inspection makes it possible to move `instanceof` with patterns to a codebase using an earlier Java version\nby applying the quick-fix.\n\n\nNote that the result can be not completely equivalent to the original `instanceof` with patterns when\na complex expression before `instanceof` is used. In this case this expression will be reevaluated.\n\nExample:\n\n\n if (object instanceof String txt && txt.length() == 1) {\n System.out.println(txt);\n } else {\n return;\n }\n System.out.println(txt);\n\nAfter the quick-fix is applied:\n\n\n if (object instanceof String && ((String) object).length() ==1) {\n String txt = (String) object;\n System.out.println(txt);\n } else {\n return;\n }\n String txt = (String) object;\n System.out.println(txt);\n\nNew in 2023.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "PatternVariablesCanBeReplacedWithCast", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ParameterTypePreventsOverriding", + "shortDescription": { + "text": "Parameter type prevents overriding" + }, + "fullDescription": { + "text": "Reports parameter types of a subclass method that have the same name as the parameter type of the corresponding super method but belong to a different package. In these cases, the subclass method cannot override the super method. Example: 'public class A {\n public void method(Object o) {}\n}\n\npublic class B extends A {\n public void method(Object o) {} // warning on parameter type\n class Object {}\n}' After the quick-fix is applied: 'public class A {\n public void method(Object o) {}\n}\n\npublic class B extends A {\n public void method(java.lang.Object o) {} // new parameter type\n class Object {}\n}'", + "markdown": "Reports parameter types of a subclass method that have the same name as the parameter type of the corresponding super method but belong to a different package. In these cases, the subclass method cannot override the super method.\n\n**Example:**\n\n\n public class A {\n public void method(Object o) {}\n }\n\n public class B extends A {\n public void method(Object o) {} // warning on parameter type\n class Object {}\n }\n\nAfter the quick-fix is applied:\n\n\n public class A {\n public void method(Object o) {}\n }\n\n public class B extends A {\n public void method(java.lang.Object o) {} // new parameter type\n class Object {}\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ParameterTypePreventsOverriding", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Inheritance issues", + "index": 25, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceInefficientStreamCount", + "shortDescription": { + "text": "Inefficient Stream API call chains ending with count()" + }, + "fullDescription": { + "text": "Reports Stream API call chains ending with the 'count()' operation that could be optimized. The following call chains are replaced by this inspection: 'Collection.stream().count()' → 'Collection.size()'. In Java 8 'Collection.stream().count()' actually iterates over the collection elements to count them, while 'Collection.size()' is much faster for most of the collections. 'Stream.flatMap(Collection::stream).count()' → 'Stream.mapToLong(Collection::size).sum()'. Similarly, there's no need to iterate over all the nested collections. Instead, their sizes could be summed up. 'Stream.filter(o -> ...).count() > 0' → 'Stream.anyMatch(o -> ...)'. Unlike the original call, 'anyMatch()' may stop the computation as soon as a matching element is found. 'Stream.filter(o -> ...).count() == 0' → 'Stream.noneMatch(o -> ...)'. Similar to the above. Note that if the replacement involves a short-circuiting operation like 'anyMatch()', there could be a visible behavior change, if the intermediate stream operations produce side effects. In general, side effects should be avoided in Stream API calls.", + "markdown": "Reports Stream API call chains ending with the `count()` operation that could be optimized.\n\n\nThe following call chains are replaced by this inspection:\n\n* `Collection.stream().count()` → `Collection.size()`. In Java 8 `Collection.stream().count()` actually iterates over the collection elements to count them, while `Collection.size()` is much faster for most of the collections.\n* `Stream.flatMap(Collection::stream).count()` → `Stream.mapToLong(Collection::size).sum()`. Similarly, there's no need to iterate over all the nested collections. Instead, their sizes could be summed up.\n* `Stream.filter(o -> ...).count() > 0` → `Stream.anyMatch(o -> ...)`. Unlike the original call, `anyMatch()` may stop the computation as soon as a matching element is found.\n* `Stream.filter(o -> ...).count() == 0` → `Stream.noneMatch(o -> ...)`. Similar to the above.\n\n\nNote that if the replacement involves a short-circuiting operation like `anyMatch()`, there could be a visible behavior change,\nif the intermediate stream operations produce side effects. In general, side effects should be avoided in Stream API calls." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ReplaceInefficientStreamCount", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ManualArrayToCollectionCopy", + "shortDescription": { + "text": "Manual array to collection copy" + }, + "fullDescription": { + "text": "Reports code that uses a loop to copy the contents of an array into a collection. A shorter and potentially faster (depending on the collection implementation) way to do this is using 'Collection.addAll(Arrays.asList())' or 'Collections.addAll()'. Only loops without additional statements inside are reported. Example: 'void addAll(List list, String[] arr) {\n for (int i = 0; i < arr.length; i++) {\n String s = arr[i];\n list.add(s);\n }\n }' After the quick-fix is applied: 'void addAll(List list, String[] arr) {\n Collections.addAll(list, arr);\n }'", + "markdown": "Reports code that uses a loop to copy the contents of an array into a collection.\n\n\nA shorter and potentially faster (depending on the collection implementation) way to do this is using `Collection.addAll(Arrays.asList())` or `Collections.addAll()`.\n\n\nOnly loops without additional statements inside are reported.\n\n**Example:**\n\n\n void addAll(List list, String[] arr) {\n for (int i = 0; i < arr.length; i++) {\n String s = arr[i];\n list.add(s);\n }\n }\n\nAfter the quick-fix is applied:\n\n\n void addAll(List list, String[] arr) {\n Collections.addAll(list, arr);\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ManualArrayToCollectionCopy", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SwitchLabeledRuleCanBeCodeBlock", + "shortDescription": { + "text": "Labeled switch rule can have code block" + }, + "fullDescription": { + "text": "Reports rules of 'switch' expressions or enhanced 'switch' statements with an expression body. These can be converted to code blocks. Example: 'String message = switch (errorCode) {\n case 404 -> \"Not found!\";\n ...\n };' After the quick-fix is applied: 'String message = switch (errorCode) {\n case 404 -> {\n yield \"Not found!\";\n }\n ...\n };' The inspection only reports if the language level of the project or module is 14 or higher. New in 2019.1", + "markdown": "Reports rules of `switch` expressions or enhanced `switch` statements with an expression body. These can be converted to code blocks.\n\nExample:\n\n\n String message = switch (errorCode) {\n case 404 -> \"Not found!\";\n ...\n };\n\nAfter the quick-fix is applied:\n\n\n String message = switch (errorCode) {\n case 404 -> {\n yield \"Not found!\";\n }\n ...\n };\n\nThe inspection only reports if the language level of the project or module is 14 or higher.\n\nNew in 2019.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "SwitchLabeledRuleCanBeCodeBlock", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ExtractMethodRecommender", + "shortDescription": { + "text": "Method can be extracted" + }, + "fullDescription": { + "text": "Suggests extracting fragments of code to a separate method to make code more clear. This inspection has a number of heuristics to select good candidates for extraction, including the following ones. The extracted fragment has no non-local control flow The extracted fragment has exactly one output variable There are no similar uses of output variable inside the extracted fragment and outside it The extracted fragment has only few input parameters (no more than three by default; configured with the inspection option) The extracted fragment is not smaller than the configured length (500 characters by default) but no bigger than 60% of the containing method body", + "markdown": "Suggests extracting fragments of code to a separate method to make code more clear. This inspection has a number of heuristics to select good candidates for extraction, including the following ones.\n\n* The extracted fragment has no non-local control flow\n* The extracted fragment has exactly one output variable\n* There are no similar uses of output variable inside the extracted fragment and outside it\n* The extracted fragment has only few input parameters (no more than three by default; configured with the inspection option)\n* The extracted fragment is not smaller than the configured length (500 characters by default) but no bigger than 60% of the containing method body" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ExtractMethodRecommender", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code maturity", + "index": 50, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringOperationCanBeSimplified", + "shortDescription": { + "text": "Redundant 'String' operation" + }, + "fullDescription": { + "text": "Reports redundant calls to 'String' constructors and methods like 'toString()' or 'substring()' that can be replaced with a simpler expression. For example, calls to these methods can be safely removed in code like '\"string\".substring(0)', '\"string\".toString()', or 'new StringBuilder().toString().substring(1,3)'. Example: 'System.out.println(new String(\"message\"));' After the quick-fix is applied: 'System.out.println(\"message\");' Note that the quick-fix removes the redundant constructor call, and this may affect 'String' referential equality. If you need to preserve it, even though it is considered bad practice, suppress the warning or use the inspection setting to ignore redundant 'String' constructor calls. Use the Do not report String constructor calls option below to not report code like the example above. This will avoid changing the outcome of String comparisons with '==' or '!=' after applying the quick-fix in code that uses 'new String()' calls to guarantee a different object identity. New in 2018.1", + "markdown": "Reports redundant calls to `String` constructors and methods like `toString()` or `substring()` that can be replaced with a simpler expression.\n\nFor example, calls to these methods can be safely removed in code\nlike `\"string\".substring(0)`, `\"string\".toString()`, or\n`new StringBuilder().toString().substring(1,3)`.\n\nExample:\n\n\n System.out.println(new String(\"message\"));\n\nAfter the quick-fix is applied:\n\n\n System.out.println(\"message\");\n\n\nNote that the quick-fix removes the redundant constructor call, and this may affect `String` referential equality.\nIf you need to preserve it, even though it is considered bad practice, suppress the warning or use the inspection setting to ignore\nredundant `String` constructor calls.\n\n\nUse the **Do not report String constructor calls** option below to not report code like the example above.\nThis will avoid changing the outcome of String comparisons with `==` or `!=` after applying\nthe quick-fix in code that uses `new String()` calls to guarantee a different object identity.\n\nNew in 2018.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StringOperationCanBeSimplified", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassReferencesSubclass", + "shortDescription": { + "text": "Class references one of its subclasses" + }, + "fullDescription": { + "text": "Reports classes which contain references to one of their subclasses. Such references may be confusing and violate several rules of object-oriented design. Example: 'class Entity {\n // Warning: the class references its subclass\n void compare(SimpleEntity entity) {\n ...\n }\n }\n class SimpleEntity extends Entity {\n ...\n }'", + "markdown": "Reports classes which contain references to one of their subclasses. Such references may be confusing and violate several rules of object-oriented design.\n\nExample:\n\n\n class Entity {\n // Warning: the class references its subclass\n void compare(SimpleEntity entity) {\n ...\n }\n }\n class SimpleEntity extends Entity {\n ...\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassReferencesSubclass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Abstraction issues", + "index": 78, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JavaReflectionMemberAccess", + "shortDescription": { + "text": "Reflective access to non-existent or not visible class member" + }, + "fullDescription": { + "text": "Reports reflective access to fields and methods that don't exist or aren't visible. Example: 'Field stringHashField() throws NoSuchFieldException {\n return String.class.getField(\"hash\");\n }' After the quick-fix is applied: 'Field stringHashField() throws NoSuchFieldException {\n return String.class.getDeclaredField(\"hash\");\n }' With a 'final' class, it's clear if there is a field or method with the specified name in the class. With non-'final' classes, it's possible that a subclass has a field or method with that name, so there could be false positives. Use the inspection's settings to get rid of such false positives everywhere or with specific classes. New in 2017.2", + "markdown": "Reports reflective access to fields and methods that don't exist or aren't visible.\n\nExample:\n\n\n Field stringHashField() throws NoSuchFieldException {\n return String.class.getField(\"hash\");\n }\n\nAfter the quick-fix is applied:\n\n\n Field stringHashField() throws NoSuchFieldException {\n return String.class.getDeclaredField(\"hash\");\n }\n\n\nWith a `final` class, it's clear if there is a field or method with the specified name in the class.\n\n\nWith non-`final` classes, it's possible that a subclass has a field or method with that name, so there could be false positives.\nUse the inspection's settings to get rid of such false positives everywhere or with specific classes.\n\nNew in 2017.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "JavaReflectionMemberAccess", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Reflective access", + "index": 98, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ObviousNullCheck", + "shortDescription": { + "text": "Null-check method is called with obviously non-null argument" + }, + "fullDescription": { + "text": "Reports if a null-checking method (for example, 'Objects.requireNonNull' or 'Assert.assertNotNull') is called on a value that is obviously non-null (for example, a newly created object). Such a check is redundant and may indicate a programming error. Example: 'final String greeting = Objects.requireNonNull(\"Hi!\");' After the quick-fix is applied: 'final String greeting = \"Hi!\";' New in 2017.2", + "markdown": "Reports if a null-checking method (for example, `Objects.requireNonNull` or `Assert.assertNotNull`) is called on a value that is obviously non-null (for example, a newly created object). Such a check is redundant and may indicate a programming error.\n\n**Example:**\n\n\n final String greeting = Objects.requireNonNull(\"Hi!\");\n\nAfter the quick-fix is applied:\n\n\n final String greeting = \"Hi!\";\n\nNew in 2017.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ObviousNullCheck", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SerialVersionUIDNotStaticFinal", + "shortDescription": { + "text": "'serialVersionUID' field not declared 'private static final long'" + }, + "fullDescription": { + "text": "Reports 'Serializable' classes whose 'serialVersionUID' field is not declared 'private static final long'. Example: 'class SampleClass implements Serializable {\n private long serialVersionUID = 1; // field of a Serializable class is not declared 'private static final long'\n\n public SampleClass() {\n System.out.println(serialVersionUID);\n }\n }'", + "markdown": "Reports `Serializable` classes whose `serialVersionUID` field is not declared `private static final long`.\n\n**Example:**\n\n\n class SampleClass implements Serializable {\n private long serialVersionUID = 1; // field of a Serializable class is not declared 'private static final long'\n\n public SampleClass() {\n System.out.println(serialVersionUID);\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SerialVersionUIDWithWrongSignature", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InnerClassOnInterface", + "shortDescription": { + "text": "Inner class of interface" + }, + "fullDescription": { + "text": "Reports inner classes in 'interface' classes. Some coding standards discourage the use of such classes. The inspection doesn't report enum classes and annotation interfaces. Use the Ignore inner interfaces of interfaces option to ignore inner interfaces. For example: 'interface I {\n interface Inner {\n }\n }'", + "markdown": "Reports inner classes in `interface` classes.\n\nSome coding standards\ndiscourage the use of such classes. The inspection doesn't report enum classes and annotation interfaces.\n\n\nUse the **Ignore inner interfaces of interfaces** option to ignore inner interfaces. For example:\n\n\n interface I {\n interface Inner {\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "InnerClassOfInterface", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DateToString", + "shortDescription": { + "text": "Call to 'Date.toString()'" + }, + "fullDescription": { + "text": "Reports 'toString()' calls on 'java.util.Date' objects. Such calls are usually incorrect in an internationalized environment.", + "markdown": "Reports `toString()` calls on `java.util.Date` objects. Such calls are usually incorrect in an internationalized environment." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToDateToString", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Internationalization", + "index": 9, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IterableUsedAsVararg", + "shortDescription": { + "text": "Iterable is used as vararg" + }, + "fullDescription": { + "text": "Reports suspicious usages of 'Collection' or 'Iterable' in vararg method calls. For example, in the following method: ' boolean contains(T needle, T... haystack) {...}' a call like 'if(contains(\"item\", listOfStrings)) {...}' looks suspicious as the list will be wrapped into a single element array. Such code can be successfully compiled and will likely run without exceptions, but it's probably used by mistake. New in 2019.2", + "markdown": "Reports suspicious usages of `Collection` or `Iterable` in vararg method calls.\n\nFor example, in the following method:\n\n\n boolean contains(T needle, T... haystack) {...}\n\na call like\n\n\n if(contains(\"item\", listOfStrings)) {...}\n\nlooks suspicious as the list will be wrapped into a single element array.\nSuch code can be successfully compiled and will likely run without\nexceptions, but it's probably used by mistake.\n\nNew in 2019.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "IterableUsedAsVararg", + "cweIds": [ + 628 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MethodNameSameAsParentName", + "shortDescription": { + "text": "Method name same as parent class name" + }, + "fullDescription": { + "text": "Reports methods that have the same name as the superclass of the method's class, as such a method name may be confusing. This inspection doesn't check interfaces or superclasses deep in the hierarchy. Example: 'class Parent {}\n class Child extends Parent {\n public Parent Parent() {\n return null;\n }\n }' A quick-fix that renames such methods is available only in the editor.", + "markdown": "Reports methods that have the same name as the superclass of the method's class, as such a method name may be confusing.\n\nThis inspection doesn't check interfaces or superclasses deep in the hierarchy.\n\n**Example:**\n\n\n class Parent {}\n class Child extends Parent {\n public Parent Parent() {\n return null;\n }\n }\n\nA quick-fix that renames such methods is available only in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MethodNameSameAsParentName", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions/Method", + "index": 88, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PublicFieldAccessedInSynchronizedContext", + "shortDescription": { + "text": "Non-private field accessed in 'synchronized' context" + }, + "fullDescription": { + "text": "Reports non-'final', non-'private' fields that are accessed in a synchronized context. A non-'private' field cannot be guaranteed to always be accessed in a synchronized manner, and such \"partially synchronized\" access may result in unexpectedly inconsistent data structures. Example: 'class Bar {\n public String field1;\n }\n public Bar myBar;\n\n synchronized public void sample() {\n myBar.field1 = \"bar\";\n }'", + "markdown": "Reports non-`final`, non-`private` fields that are accessed in a synchronized context.\n\n\nA non-`private` field cannot be guaranteed to always be accessed in a synchronized manner, and such \"partially synchronized\"\naccess may result in unexpectedly inconsistent data structures.\n\n**Example:**\n\n\n class Bar {\n public String field1;\n }\n public Bar myBar;\n\n synchronized public void sample() {\n myBar.field1 = \"bar\";\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonPrivateFieldAccessedInSynchronizedContext", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnusedLabel", + "shortDescription": { + "text": "Unused label" + }, + "fullDescription": { + "text": "Reports labels that are not targets of any 'break' or 'continue' statements. Example: 'label: for (int i = 0; i < 10; i++) {\n if (i == 3) {\n break;\n }\n }' After the quick-fix is applied, the label is removed: 'for (int i = 0; i < 10; i++) {\n if (i == 3) {\n break;\n }\n }'", + "markdown": "Reports labels that are not targets of any `break` or `continue` statements.\n\n**Example:**\n\n\n label: for (int i = 0; i < 10; i++) {\n if (i == 3) {\n break;\n }\n }\n\nAfter the quick-fix is applied, the label is removed:\n\n\n for (int i = 0; i < 10; i++) {\n if (i == 3) {\n break;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnusedLabel", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ForeachStatement", + "shortDescription": { + "text": "Enhanced 'for' statement" + }, + "fullDescription": { + "text": "Reports enhanced 'for' statements. Example: 'for (int x: Arrays.asList(1, 2, 3)) {\n System.out.println(x);\n }' After the quick-fix is applied: 'for (Iterator iterator = Arrays.asList(1, 2, 3).iterator(); iterator.hasNext(); ) {\n final int x = iterator.next();\n System.out.println(x);\n }' Enhanced 'for' statement appeared in Java 5. This inspection can help to downgrade for backward compatibility with earlier Java versions.", + "markdown": "Reports enhanced `for` statements.\n\nExample:\n\n\n for (int x: Arrays.asList(1, 2, 3)) {\n System.out.println(x);\n }\n\nAfter the quick-fix is applied:\n\n\n for (Iterator iterator = Arrays.asList(1, 2, 3).iterator(); iterator.hasNext(); ) {\n final int x = iterator.next();\n System.out.println(x);\n }\n\n\n*Enhanced* `for` *statement* appeared in Java 5.\nThis inspection can help to downgrade for backward compatibility with earlier Java versions." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ForeachStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level issues", + "index": 64, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LambdaParameterNamingConvention", + "shortDescription": { + "text": "Lambda parameter naming convention" + }, + "fullDescription": { + "text": "Reports lambda parameters whose names are too short, too long, or do not follow the specified regular expression pattern. Example: 'Function id = X -> X;' should be reported if the inspection is enabled with the default settings in which a parameter name should start with a lowercase letter. Configure the inspection: Use the fields in the Options section to specify the minimum length, maximum length, and a regular expression expected for lambda parameter names. Specify 0 in order not to check the length of names. Regular expressions should be specified in the standard 'java.util.regex' format.", + "markdown": "Reports lambda parameters whose names are too short, too long, or do not follow the specified regular expression pattern.\n\n**Example:** `Function id = X -> X;`\nshould be reported if the inspection is enabled with the default settings in which a parameter name should start with a lowercase letter.\n\nConfigure the inspection:\n\n\nUse the fields in the **Options** section to specify the minimum length, maximum length, and a regular expression expected for lambda parameter names.\nSpecify **0** in order not to check the length of names.\n\nRegular expressions should be specified in the standard `java.util.regex` format." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LambdaParameterNamingConvention", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions", + "index": 51, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LawOfDemeter", + "shortDescription": { + "text": "Law of Demeter" + }, + "fullDescription": { + "text": "Reports Law of Demeter violations. The Law of Demeter is not really a law, but specifies a style guideline: never call a method on an object received from another call. The code that follows this guideline is easier to maintain, adapt, and refactor, has less coupling between methods, less duplication, and better information hiding. On the other hand, you may need to write many wrapper methods to meet this guideline. Example: 'boolean pay(Customer c, Invoice invoice) {\n int dollars = c.getWallet().contents; // violation\n if (dollars >= invoice.getAmount()) {\n Wallet w = c.getWallet();\n w.subtract(invoice.getAmount()); // violation\n return true;\n }\n return false;\n }' The above example might be better implemented as a method 'payInvoice(Invoice invoice)' in 'Customer'. Use the Ignore calls to library methods and access to library fields option to ignore Law of Demeter violations that can't be fixed without changing a library.", + "markdown": "Reports [Law of Demeter](https://en.wikipedia.org/wiki/Law_of_Demeter) violations.\n\n\nThe Law of Demeter is not really a law, but specifies a style guideline: never call a method on an object received from another call.\nThe code that follows this guideline is easier to maintain, adapt, and refactor, has less coupling between methods, less duplication,\nand better information hiding. On the other hand, you may need to write many wrapper methods to meet this guideline.\n\n**Example:**\n\n\n boolean pay(Customer c, Invoice invoice) {\n int dollars = c.getWallet().contents; // violation\n if (dollars >= invoice.getAmount()) {\n Wallet w = c.getWallet();\n w.subtract(invoice.getAmount()); // violation\n return true;\n }\n return false;\n }\n\nThe above example might be better implemented as a method `payInvoice(Invoice invoice)` in `Customer`.\n\n\nUse the **Ignore calls to library methods and access to library fields** option to ignore Law of Demeter violations\nthat can't be fixed without changing a library." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LawOfDemeter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Data flow", + "index": 23, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OptionalUsedAsFieldOrParameterType", + "shortDescription": { + "text": "'Optional' used as field or parameter type" + }, + "fullDescription": { + "text": "Reports any cases in which 'java.util.Optional', 'java.util.OptionalDouble', 'java.util.OptionalInt', 'java.util.OptionalLong', or 'com.google.common.base.Optional' are used as types for fields or parameters. 'Optional' was designed to provide a limited mechanism for library method return types in which a clear way to represent \"no result\" was needed. Using a field with the 'java.util.Optional' type is also problematic if the class needs to be 'Serializable', as 'java.util.Optional' is not serializable. Example: 'class MyClass {\n Optional name; // Optional field\n\n // Optional parameter\n void setName(Optional name) {\n this.name = name;\n }\n }'", + "markdown": "Reports any cases in which `java.util.Optional`, `java.util.OptionalDouble`, `java.util.OptionalInt`, `java.util.OptionalLong`, or `com.google.common.base.Optional` are used as types for fields or parameters.\n\n`Optional` was designed to provide a limited mechanism for library method return types in which a clear way to represent \"no result\"\nwas needed.\n\nUsing a field with the `java.util.Optional` type is also problematic if the class needs to be\n`Serializable`, as `java.util.Optional` is not serializable.\n\nExample:\n\n\n class MyClass {\n Optional name; // Optional field\n\n // Optional parameter\n void setName(Optional name) {\n this.name = name;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "OptionalUsedAsFieldOrParameterType", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Abstraction issues", + "index": 78, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceAllDot", + "shortDescription": { + "text": "Suspicious regex expression argument" + }, + "fullDescription": { + "text": "Reports calls to 'String.replaceAll()' or 'String.split()' where the first argument is a single regex meta character argument. The regex meta characters are one of '.$|()[{^?*+\\'. They have a special meaning in regular expressions. For example, calling '\"ab.cd\".replaceAll(\".\", \"-\")' produces '\"-----\"', because the dot matches any character. Most likely the escaped variant '\"\\\\.\"' was intended instead. Using 'File.separator' as a regex is also reported. The 'File.separator' has a platform specific value. It equals to '/' on Linux and Mac but equals to '\\' on Windows, which is not a valid regular expression, so such code is not portable. Example: 's.replaceAll(\".\", \"-\");' After the quick-fix is applied: 's.replaceAll(\"\\\\.\", \"-\");'", + "markdown": "Reports calls to `String.replaceAll()` or `String.split()` where the first argument is a single regex meta character argument.\n\n\nThe regex meta characters are one of `.$|()[{^?*+\\`. They have a special meaning in regular expressions.\nFor example, calling `\"ab.cd\".replaceAll(\".\", \"-\")` produces `\"-----\"`, because the dot matches any character.\nMost likely the escaped variant `\"\\\\.\"` was intended instead.\n\n\nUsing `File.separator` as a regex is also reported. The `File.separator` has a platform specific value. It\nequals to `/` on Linux and Mac but equals to `\\` on Windows, which is not a valid regular expression, so\nsuch code is not portable.\n\n**Example:**\n\n\n s.replaceAll(\".\", \"-\");\n\nAfter the quick-fix is applied:\n\n\n s.replaceAll(\"\\\\.\", \"-\");\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SuspiciousRegexArgument", + "cweIds": [ + 20, + 185, + 628 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ForCanBeForeach", + "shortDescription": { + "text": "'for' loop can be replaced with enhanced for loop" + }, + "fullDescription": { + "text": "Reports 'for' loops that iterate over collections or arrays, and can be automatically replaced with an enhanced 'for' loop (foreach iteration syntax). Example: 'for (Iterator iterator = list.iterator(); iterator.hasNext(); ) {\n String item = iterator.next();\n System.out.println(item);\n }' After the quick-fix is applied: 'for (String item : list) {\n System.out.println(item);\n }' Use the Report indexed 'java.util.List' loops option to find loops involving 'list.get(index)' calls. Generally, these loops can be replaced with enhanced 'for' loops, unless they modify an underlying list in the process, for example, by calling 'list.remove(index)'. If the latter is the case, the enhanced 'for' loop may throw 'ConcurrentModificationException'. Also, in some cases, 'list.get(index)' loops may work a little bit faster. Use the Do not report iterations over untyped collections option to ignore collections without type parameters. This prevents the creation of enhanced 'for' loop variables of the 'java.lang.Object' type and the insertion of casts where the loop variable is used. This inspection only reports if the language level of the project or module is 5 or higher.", + "markdown": "Reports `for` loops that iterate over collections or arrays, and can be automatically replaced with an enhanced `for` loop (foreach iteration syntax).\n\n**Example:**\n\n\n for (Iterator iterator = list.iterator(); iterator.hasNext(); ) {\n String item = iterator.next();\n System.out.println(item);\n }\n\nAfter the quick-fix is applied:\n\n\n for (String item : list) {\n System.out.println(item);\n }\n\n\nUse the **Report indexed 'java.util.List' loops** option to find loops involving `list.get(index)` calls.\nGenerally, these loops can be replaced with enhanced `for` loops,\nunless they modify an underlying list in the process, for example, by calling `list.remove(index)`.\nIf the latter is the case, the enhanced `for` loop may throw `ConcurrentModificationException`.\nAlso, in some cases, `list.get(index)` loops may work a little bit faster.\n\n\nUse the **Do not report iterations over untyped collections** option to ignore collections without type parameters.\nThis prevents the creation of enhanced `for` loop variables of the `java.lang.Object` type and the insertion of casts\nwhere the loop variable is used.\n\nThis inspection only reports if the language level of the project or module is 5 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ForLoopReplaceableByForEach", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 5", + "index": 53, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AmbiguousFieldAccess", + "shortDescription": { + "text": "Access to inherited field looks like access to element from surrounding code" + }, + "fullDescription": { + "text": "Reports access to a superclass field from an anonymous, inner or local class, if a local variable, parameter, or field with the same name is available in the code surrounding the class. In this case it may seem that an element from the surrounding code is accessed, when in fact it is an access to a field from the superclass. To clarify the intent of the code, it is recommended to add an explicit 'super' qualifier to the field access. Example: 'class First {\n protected String ambiguous;\n }\n class Second {\n void foo(String ambiguous) {\n new First() {\n {\n System.out.println(ambiguous); // the field is accessed, not the parameter\n }\n };\n }\n }' After the quick-fix is applied: 'class First {\n protected String ambiguous;\n }\n class Second {\n void foo(String ambiguous) {\n new First() {\n {\n System.out.println(super.ambiguous);\n }\n };\n }\n }'", + "markdown": "Reports access to a superclass field from an anonymous, inner or local class, if a local variable, parameter, or field with the same name is available in the code surrounding the class. In this case it may seem that an element from the surrounding code is accessed, when in fact it is an access to a field from the superclass.\n\n\nTo clarify the intent of the code, it is recommended to add an explicit\n`super` qualifier to the field access.\n\n**Example:**\n\n\n class First {\n protected String ambiguous;\n }\n class Second {\n void foo(String ambiguous) {\n new First() {\n {\n System.out.println(ambiguous); // the field is accessed, not the parameter\n }\n };\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class First {\n protected String ambiguous;\n }\n class Second {\n void foo(String ambiguous) {\n new First() {\n {\n System.out.println(super.ambiguous);\n }\n };\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AmbiguousFieldAccess", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Visibility", + "index": 57, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonProtectedConstructorInAbstractClass", + "shortDescription": { + "text": "Public constructor in abstract class" + }, + "fullDescription": { + "text": "Reports 'public' constructors of 'abstract' classes. Constructors of 'abstract' classes can only be called from the constructors of their subclasses, declaring them 'public' may be confusing. The quick-fix makes such constructors protected. Example: 'public abstract class Foo {\n public Foo () { // warning: has 'public' modifier\n /* ... */\n }\n }' After the quick-fix is applied: 'public abstract class Foo {\n protected Foo () {\n /* ... */\n }\n }' Configure the inspection: Use the Ignore for non-public classes option below to ignore 'public' constructors in non-public classes.", + "markdown": "Reports `public` constructors of `abstract` classes.\n\n\nConstructors of `abstract` classes can only be called from the constructors of\ntheir subclasses, declaring them `public` may be confusing.\n\nThe quick-fix makes such constructors protected.\n\n**Example:**\n\n\n public abstract class Foo {\n public Foo () { // warning: has 'public' modifier\n /* ... */\n }\n }\n\nAfter the quick-fix is applied:\n\n\n public abstract class Foo {\n protected Foo () {\n /* ... */\n }\n }\n\nConfigure the inspection:\n\nUse the **Ignore for non-public classes** option below to ignore `public` constructors in non-public classes." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ConstructorNotProtectedInAbstractClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Inheritance issues", + "index": 25, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PackageVisibleField", + "shortDescription": { + "text": "Package-visible field" + }, + "fullDescription": { + "text": "Reports fields that are declared without any access modifier (also known as package-private). Constants (that is, fields marked 'static' and 'final') are not reported. Example: 'public class A {\n Object object; // warning\n final static int MODE = 0; // constant, no warning\n }'", + "markdown": "Reports fields that are declared without any access modifier (also known as package-private).\n\nConstants (that is, fields marked `static` and `final`) are not reported.\n\n**Example:**\n\n\n public class A {\n Object object; // warning\n final static int MODE = 0; // constant, no warning\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PackageVisibleField", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Encapsulation", + "index": 60, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ComparableImplementedButEqualsNotOverridden", + "shortDescription": { + "text": "'Comparable' implemented but 'equals()' not overridden" + }, + "fullDescription": { + "text": "Reports classes that implement 'java.lang.Comparable' but do not override 'equals()'. If 'equals()' is not overridden, the 'equals()' implementation is not consistent with the 'compareTo()' implementation. If an object of such a class is added to a collection such as 'java.util.SortedSet', this collection will violate the contract of 'java.util.Set', which is defined in terms of 'equals()'. Example: 'class Length implements Comparable {\n private int cm = 0;\n\n @Override\n public int compareTo(@NotNull Length o) {\n if (cm == o.cm) return 0;\n return cm < o.cm ? -1 : 1;\n }\n }' After the quick fix is applied: 'class Length implements Comparable {\n private int cm = 0;\n\n @Override\n public int compareTo(@NotNull Length o) {\n if (cm == o.cm) return 0;\n return cm < o.cm ? -1 : 1;\n }\n\n @Override\n public boolean equals(Object o) {\n return o instanceof Length && compareTo((Length) o) == 0;\n }\n }'", + "markdown": "Reports classes that implement `java.lang.Comparable` but do not override `equals()`.\n\n\nIf `equals()`\nis not overridden, the `equals()` implementation is not consistent with\nthe `compareTo()` implementation. If an object of such a class is added\nto a collection such as `java.util.SortedSet`, this collection will violate\nthe contract of `java.util.Set`, which is defined in terms of\n`equals()`.\n\n**Example:**\n\n\n class Length implements Comparable {\n private int cm = 0;\n\n @Override\n public int compareTo(@NotNull Length o) {\n if (cm == o.cm) return 0;\n return cm < o.cm ? -1 : 1;\n }\n }\n\nAfter the quick fix is applied:\n\n\n class Length implements Comparable {\n private int cm = 0;\n\n @Override\n public int compareTo(@NotNull Length o) {\n if (cm == o.cm) return 0;\n return cm < o.cm ? -1 : 1;\n }\n\n @Override\n public boolean equals(Object o) {\n return o instanceof Length && compareTo((Length) o) == 0;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ComparableImplementedButEqualsNotOverridden", + "cweIds": [ + 697 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MapReplaceableByEnumMap", + "shortDescription": { + "text": "'Map' can be replaced with 'EnumMap'" + }, + "fullDescription": { + "text": "Reports instantiations of 'java.util.Map' objects whose key types are enumerated classes. Such 'java.util.Map' objects can be replaced with 'java.util.EnumMap' objects. 'java.util.EnumMap' implementations can be much more efficient because the underlying data structure is a simple array. Example: 'Map myEnums = new HashMap<>();' After the quick-fix is applied: 'Map myEnums = new EnumMap<>(MyEnum.class);'", + "markdown": "Reports instantiations of `java.util.Map` objects whose key types are enumerated classes. Such `java.util.Map` objects can be replaced with `java.util.EnumMap` objects.\n\n\n`java.util.EnumMap` implementations can be much more efficient\nbecause the underlying data structure is a simple array.\n\n**Example:**\n\n\n Map myEnums = new HashMap<>();\n\nAfter the quick-fix is applied:\n\n\n Map myEnums = new EnumMap<>(MyEnum.class);\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MapReplaceableByEnumMap", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InstantiationOfUtilityClass", + "shortDescription": { + "text": "Instantiation of utility class" + }, + "fullDescription": { + "text": "Reports instantiation of utility classes using the 'new' keyword. In utility classes, all fields and methods are 'static'. Instantiation of such classes is most likely unnecessary and indicates a mistake. Example: 'class MyUtils {\n public static double cube(double x) {\n return x * x * x;\n }\n }\n class Main {\n public static void main(String[] args) {\n // Instantiation of utility class\n MyUtils utils = new MyUtils();\n }\n }' To prevent utility classes from being instantiated, it's recommended to use a 'private' constructor.", + "markdown": "Reports instantiation of utility classes using the `new` keyword.\n\n\nIn utility classes, all fields and methods are `static`.\nInstantiation of such classes is most likely unnecessary and indicates a mistake.\n\n**Example:**\n\n\n class MyUtils {\n public static double cube(double x) {\n return x * x * x;\n }\n }\n class Main {\n public static void main(String[] args) {\n // Instantiation of utility class\n MyUtils utils = new MyUtils();\n }\n }\n\n\nTo prevent utility classes from being instantiated,\nit's recommended to use a `private` constructor." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "InstantiationOfUtilityClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TrailingWhitespacesInTextBlock", + "shortDescription": { + "text": "Trailing whitespace in text block" + }, + "fullDescription": { + "text": "Reports text blocks with trailing whitespace characters. Trailing whitespace is considered incidental and will be stripped away by the Java compiler. This inspection only reports if the language level of the project or module is 15 or higher. New in 2021.1", + "markdown": "Reports text blocks with trailing whitespace characters. Trailing whitespace is considered incidental and will be stripped away by the Java compiler.\n\nThis inspection only reports if the language level of the project or module is 15 or higher.\n\nNew in 2021.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "TrailingWhitespacesInTextBlock", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReturnNull", + "shortDescription": { + "text": "Return of 'null'" + }, + "fullDescription": { + "text": "Reports 'return' statements with 'null' return values. While occasionally useful, this construct may make the code more prone to failing with a 'NullPointerException'. If a method is designed to return 'null', it is suggested to mark it with the '@Nullable' annotation - such methods will be ignored by this inspection. Example: 'class Person {\n public String getName () {\n return null;\n }\n }' After the quick-fix is applied: 'class Person {\n @Nullable\n public String getName () {\n return null;\n }\n }' If the return type is 'java.util.Optional', an additional quick-fix to convert 'null' to 'Optional.empty()' is suggested. Use the following options to configure the inspection: Whether to ignore 'private' methods. This will also ignore return of 'null' from anonymous classes and lambdas. Whether 'null' values on array returns, collection object returns, plain object returns, or a combination of the three should be reported. Return of 'null' in methods with return type 'java.util.Optional' are always reported. Click Configure annotations to specify which annotations should be considered 'nullable'.", + "markdown": "Reports `return` statements with `null` return values. While occasionally useful, this construct may make the code more prone to failing with a `NullPointerException`.\n\n\nIf a method is designed to return `null`, it is suggested to mark it with the\n`@Nullable` annotation - such methods will be ignored by this inspection.\n\n**Example:**\n\n\n class Person {\n public String getName () {\n return null;\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Person {\n @Nullable\n public String getName () {\n return null;\n }\n }\n\n\nIf the return type is `java.util.Optional`, an additional quick-fix to convert\n`null` to `Optional.empty()` is suggested.\n\n\nUse the following options to configure the inspection:\n\n* Whether to ignore `private` methods. This will also ignore return of `null` from anonymous classes and lambdas.\n* Whether `null` values on array returns, collection object returns, plain object returns, or a combination of the three should be reported. Return of `null` in methods with return type `java.util.Optional` are always reported.\n* Click **Configure annotations** to specify which annotations should be considered 'nullable'." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ReturnOfNull", + "cweIds": [ + 252, + 476 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs/Nullability problems", + "index": 115, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NewClassNamingConvention", + "shortDescription": { + "text": "Class naming convention" + }, + "fullDescription": { + "text": "Reports classes whose names are too short, too long, or do not follow the specified regular expression pattern. Example: if the inspection is enabled for tests, and the specified length for the minimum class name is 8 (the default), the following test class produces a warning because the length of its name is 6, which is less than 8: 'public class MyTest{}'. A quick-fix that renames such classes is available only in the editor. Configure the inspection: Use the list in the Options section to specify which classes should be checked. Deselect the checkboxes for the classes for which you want to skip the check. For each class type, specify the minimum length, maximum length, and the regular expression expected for class names using the provided input fields. Specify 0 in the length fields to skip corresponding checks. Regular expressions should be specified in the standard 'java.util.regex' format.", + "markdown": "Reports classes whose names are too short, too long, or do not follow the specified regular expression pattern.\n\n**Example:** if the inspection is enabled for tests, and the specified length for the minimum class name is 8 (the default), the following test class\nproduces a warning because the length of its name is 6, which is less than 8: `public class MyTest{}`.\n\nA quick-fix that renames such classes is available only in the editor.\n\nConfigure the inspection:\n\n\nUse the list in the **Options** section to specify which classes should be checked. Deselect the checkboxes for the classes for which\nyou want to skip the check.\n\nFor each class type, specify the minimum length, maximum length, and the regular expression expected for class names using the\nprovided input fields. Specify **0** in the length fields to skip corresponding checks.\n\nRegular expressions should be specified in the standard `java.util.regex` format." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NewClassNamingConvention", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions/Class", + "index": 71, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LoggingStringTemplateAsArgument", + "shortDescription": { + "text": "String template as argument to logging call" + }, + "fullDescription": { + "text": "Reports string templates that are used as arguments to SLF4J and Log4j 2 logging methods. The method 'org.apache.logging.log4j.Logger.log()' and its overloads are supported only for all log levels option. String templates are evaluated at runtime even when the logging message is not logged; this can negatively impact performance. It is recommended to use a parameterized log message instead, which will not be evaluated when logging is disabled. Example (for Kotlin): 'val variable1 = getVariable()\n logger.info(\"variable1: $variable1\")' After the quick-fix is applied (for Kotlin): 'val variable1 = getVariable()\n logger.info(\"variable1: {}\", variable1)' Note that the suggested replacement might not be equivalent to the original code, for example, when string templates contain method calls or assignment expressions. Use the Warn on list to ignore certain higher logging levels. Higher logging levels may be always enabled, and the arguments will always be evaluated. Use the Do not warn when only expressions with primitive types, their wrappers or String are included option to ignore string templates, which contain only expressions with primitive types, their wrappers or String. For example, it could be useful to prevent loading lazy collections. Note that, creating string even only with expressions with primitive types, their wrappers or String at runtime can negatively impact performance. New in 2023.1", + "markdown": "Reports string templates that are used as arguments to **SLF4J** and **Log4j 2** logging methods. The method `org.apache.logging.log4j.Logger.log()` and its overloads are supported only for **all log levels** option. String templates are evaluated at runtime even when the logging message is not logged; this can negatively impact performance. It is recommended to use a parameterized log message instead, which will not be evaluated when logging is disabled.\n\n**Example (for Kotlin):**\n\n\n val variable1 = getVariable()\n logger.info(\"variable1: $variable1\")\n\n**After the quick-fix is applied (for Kotlin):**\n\n\n val variable1 = getVariable()\n logger.info(\"variable1: {}\", variable1)\n\n\nNote that the suggested replacement might not be equivalent to the original code, for example,\nwhen string templates contain method calls or assignment expressions.\n\n* Use the **Warn on** list to ignore certain higher logging levels. Higher logging levels may be always enabled, and the arguments will always be evaluated.\n* Use the **Do not warn when only expressions with primitive types, their wrappers or String are included** option to ignore string templates, which contain only expressions with primitive types, their wrappers or String. For example, it could be useful to prevent loading lazy collections. Note that, creating string even only with expressions with primitive types, their wrappers or String at runtime can negatively impact performance.\n\nNew in 2023.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "LoggingStringTemplateAsArgument", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages/Logging", + "index": 46, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnpredictableBigDecimalConstructorCall", + "shortDescription": { + "text": "Unpredictable 'BigDecimal' constructor call" + }, + "fullDescription": { + "text": "Reports calls to 'BigDecimal' constructors that accept a 'double' value. These constructors produce 'BigDecimal' that is exactly equal to the supplied 'double' value. However, because doubles are encoded in the IEEE 754 64-bit double-precision binary floating-point format, the exact value can be unexpected. For example, 'new BigDecimal(0.1)' yields a 'BigDecimal' object. Its value is '0.1000000000000000055511151231257827021181583404541015625' which is the nearest number to 0.1 representable as a double. To get 'BigDecimal' that stores the same value as written in the source code, use either 'new BigDecimal(\"0.1\")' or 'BigDecimal.valueOf(0.1)'. Example: 'class Constructor {\n void foo() {\n new BigDecimal(0.1);\n }\n }' After the quick-fix is applied: 'class Constructor {\n void foo() {\n new BigDecimal(\"0.1\");\n }\n }'", + "markdown": "Reports calls to `BigDecimal` constructors that accept a `double` value. These constructors produce `BigDecimal` that is exactly equal to the supplied `double` value. However, because doubles are encoded in the IEEE 754 64-bit double-precision binary floating-point format, the exact value can be unexpected.\n\nFor example, `new BigDecimal(0.1)` yields a `BigDecimal` object. Its value is\n`0.1000000000000000055511151231257827021181583404541015625`\nwhich is the nearest number to 0.1 representable as a double.\nTo get `BigDecimal` that stores the same value as written in the source code,\nuse either `new BigDecimal(\"0.1\")` or `BigDecimal.valueOf(0.1)`.\n\n**Example:**\n\n\n class Constructor {\n void foo() {\n new BigDecimal(0.1);\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Constructor {\n void foo() {\n new BigDecimal(\"0.1\");\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "UnpredictableBigDecimalConstructorCall", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UseCompareMethod", + "shortDescription": { + "text": "'compare()' method can be used to compare numbers" + }, + "fullDescription": { + "text": "Reports expressions that can be replaced by a call to the 'Integer.compare()' method or a similar method from the 'Long', 'Short', 'Byte', 'Double' or 'Float' classes, instead of more verbose or less efficient constructs. If 'x' and 'y' are boxed integers, then 'x.compareTo(y)' is suggested, if they are primitives 'Integer.compare(x, y)' is suggested. Example: 'public int compare(int x, int y) {\n return x > y ? 1 : x < y ? -1 : 0;\n }' After the quick-fix is applied: 'public int compare(int x, int y) {\n return Integer.compare(x, y);\n }' Note that 'Double.compare' and 'Float.compare' slightly change the code semantics. In particular, they make '-0.0' and '0.0' distinguishable ('Double.compare(-0.0, 0.0)' yields -1). Also, they consistently process 'NaN' value. In most of the cases, this semantics change actually improves the code. Use the checkbox to disable this inspection for floating point numbers if semantics change is unacceptable in your case. New in 2017.2", + "markdown": "Reports expressions that can be replaced by a call to the `Integer.compare()` method or a similar method from the `Long`, `Short`, `Byte`, `Double` or `Float` classes, instead of more verbose or less efficient constructs.\n\nIf `x` and `y` are boxed integers, then `x.compareTo(y)` is suggested,\nif they are primitives `Integer.compare(x, y)` is suggested.\n\n**Example:**\n\n\n public int compare(int x, int y) {\n return x > y ? 1 : x < y ? -1 : 0;\n }\n\nAfter the quick-fix is applied:\n\n\n public int compare(int x, int y) {\n return Integer.compare(x, y);\n }\n\n\nNote that `Double.compare` and `Float.compare` slightly change the code semantics. In particular,\nthey make `-0.0` and `0.0` distinguishable (`Double.compare(-0.0, 0.0)` yields -1).\nAlso, they consistently process `NaN` value. In most of the cases, this semantics change actually improves the\ncode. Use the checkbox to disable this inspection for floating point numbers if semantics change is unacceptable\nin your case.\n\nNew in 2017.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UseCompareMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids", + "index": 52, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IntegerDivisionInFloatingPointContext", + "shortDescription": { + "text": "Integer division in floating-point context" + }, + "fullDescription": { + "text": "Reports integer divisions where the result is used as a floating-point number. Such division is often an error and may have unexpected results due to the truncation that happens in integer division. Example: 'float x = 3.0F + 3 * 2 / 5;' After the quick-fix is applied: 'float x = 3.0F + ((float) (3 * 2)) /5;'", + "markdown": "Reports integer divisions where the result is used as a floating-point number. Such division is often an error and may have unexpected results due to the truncation that happens in integer division.\n\n**Example:**\n\n\n float x = 3.0F + 3 * 2 / 5;\n\nAfter the quick-fix is applied:\n\n\n float x = 3.0F + ((float) (3 * 2)) /5;\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "IntegerDivisionInFloatingPointContext", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AssertWithoutMessage", + "shortDescription": { + "text": "Message missing on assertion" + }, + "fullDescription": { + "text": "Reports calls to 'assertXXX()' or 'fail()' without an error message string argument. An error message on assertion failure may help clarify the test case's intent. Example: 'assertTrue(checkValid());' After the quick-fix is applied: 'assertTrue(checkValid(), \"|\");' The message argument is added before or after the existing arguments according to the assertions framework that you use.", + "markdown": "Reports calls to `assertXXX()` or `fail()` without an error message string argument. An error message on assertion failure may help clarify the test case's intent.\n\n**Example:**\n\n\n assertTrue(checkValid());\n\nAfter the quick-fix is applied:\n\n assertTrue(checkValid(), \"|\");\n\n\nThe message argument is added before or after the existing arguments according to the assertions framework that you use." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AssertWithoutMessage", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Test frameworks", + "index": 96, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MoveFieldAssignmentToInitializer", + "shortDescription": { + "text": "Field assignment can be moved to initializer" + }, + "fullDescription": { + "text": "Suggests replacing initialization of fields using assignment with initialization in the field declaration. Only reports if the field assignment is located in an instance or static initializer, and joining it with the field declaration is likely to be safe. In other cases, like assignment inside a constructor, the quick-fix is provided without highlighting, as the fix may change the semantics. Example: 'class MyClass {\n static final int intConstant;\n \n static {\n intConstant = 10;\n }\n }' The quick fix moves the assigned value to the field initializer removing the class initializer if possible: 'class MyClass {\n static final int intConstant = 10;\n }' Since 2017.2", + "markdown": "Suggests replacing initialization of fields using assignment with initialization in the field declaration.\n\nOnly reports if the field assignment is located in an instance or static initializer, and\njoining it with the field declaration is likely to be safe.\nIn other cases, like assignment inside a constructor, the quick-fix is provided without highlighting,\nas the fix may change the semantics.\n\nExample:\n\n\n class MyClass {\n static final int intConstant;\n \n static {\n intConstant = 10;\n }\n }\n\nThe quick fix moves the assigned value to the field initializer removing the class initializer if possible:\n\n\n class MyClass {\n static final int intConstant = 10;\n }\n\nSince 2017.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "MoveFieldAssignmentToInitializer", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConditionalBreakInInfiniteLoop", + "shortDescription": { + "text": "Conditional break inside loop" + }, + "fullDescription": { + "text": "Reports conditional breaks at the beginning or at the end of a loop and suggests adding a loop condition instead to shorten the code. Example: 'while (true) {\n if (i == 23) break;\n i++;\n }' After the quick fix is applied: 'while (i != 23) {\n i++;\n }'", + "markdown": "Reports conditional breaks at the beginning or at the end of a loop and suggests adding a loop condition instead to shorten the code.\n\nExample:\n\n\n while (true) {\n if (i == 23) break;\n i++;\n }\n\nAfter the quick fix is applied:\n\n\n while (i != 23) {\n i++;\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ConditionalBreakInInfiniteLoop", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringConcatenationInFormatCall", + "shortDescription": { + "text": "String concatenation as argument to 'format()' call" + }, + "fullDescription": { + "text": "Reports non-constant string concatenations used as a format string argument. While occasionally intended, this is usually a misuse of a formatting method and may even cause security issues if the variables used in the concatenated string contain special characters like '%'. Also, sometimes this could be the result of mistakenly concatenating a string format argument by typing a '+' when a ',' was meant. Example: 'static String formatGreeting(String userName) {\n return String.format(\"Hello, \" + userName);\n }' Here, the 'userName' will be interpreted as a part of format string, which may result in 'IllegalFormatException' (for example, if 'userName' is '\"%\"') or in using an enormous amount of memory (for example, if 'userName' is '\"%2000000000%\"'). The call should be probably replaced with 'String.format(\"Hello, %s\", userName);'. This inspection checks calls to formatting methods on 'java.util.Formatter', 'java.lang.String', 'java.io.PrintWriter', or 'java.io.PrintStream'.", + "markdown": "Reports non-constant string concatenations used as a format string argument.\n\n\nWhile occasionally intended, this is usually a misuse of a formatting method\nand may even cause security issues if the variables used in the concatenated string\ncontain special characters like `%`.\n\n\nAlso, sometimes this could be the result\nof mistakenly concatenating a string format argument by typing a `+` when a `,` was meant.\n\n**Example:**\n\n\n static String formatGreeting(String userName) {\n return String.format(\"Hello, \" + userName);\n }\n\n\nHere, the `userName` will be interpreted as a part of format string, which may result\nin `IllegalFormatException` (for example, if `userName` is `\"%\"`) or\nin using an enormous amount of memory (for example, if `userName` is `\"%2000000000%\"`).\nThe call should be probably replaced with `String.format(\"Hello, %s\", userName);`.\n\n\nThis inspection checks calls to formatting methods on\n`java.util.Formatter`,\n`java.lang.String`,\n`java.io.PrintWriter`,\nor `java.io.PrintStream`." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StringConcatenationInFormatCall", + "cweIds": [ + 116, + 134 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnqualifiedInnerClassAccess", + "shortDescription": { + "text": "Unqualified inner class access" + }, + "fullDescription": { + "text": "Reports references to inner classes that are not qualified with the name of the enclosing class. Example: 'import foo.Foo.Bar;\n\n class Foo {\n class Bar {}\n }\n\n class Baz {\n void f(Bar bar) {}\n }' After the quick-fix is applied: 'class Foo {\n class Bar {}\n }\n\n class Baz {\n void f(Foo.Bar bar) {}\n }' Use the inspection settings to ignore references to inner classes within the same class, which therefore do not require an import.", + "markdown": "Reports references to inner classes that are not qualified with the name of the enclosing class.\n\n**Example:**\n\n\n import foo.Foo.Bar;\n\n class Foo {\n class Bar {}\n }\n\n class Baz {\n void f(Bar bar) {}\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo {\n class Bar {}\n }\n\n class Baz {\n void f(Foo.Bar bar) {}\n }\n\n\nUse the inspection settings to ignore references to inner classes within the same class,\nwhich therefore do not require an import." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnqualifiedInnerClassAccess", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantClassCall", + "shortDescription": { + "text": "Redundant 'isInstance()' or 'cast()' call" + }, + "fullDescription": { + "text": "Reports redundant calls of 'java.lang.Class' methods. For example, 'Xyz.class.isInstance(object)' can be replaced with 'object instanceof Xyz'. The instanceof check is preferred: even though the performance will probably be the same as these methods are intrinsics, they better indicate a static check. New in 2018.2", + "markdown": "Reports redundant calls of `java.lang.Class` methods.\n\nFor example, `Xyz.class.isInstance(object)` can be replaced with `object instanceof Xyz`.\nThe instanceof check is preferred: even though the performance will probably be the same as these methods are intrinsics,\nthey better indicate a static check.\n\nNew in 2018.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantClassCall", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "WaitWhileHoldingTwoLocks", + "shortDescription": { + "text": "'wait()' while holding two locks" + }, + "fullDescription": { + "text": "Reports calls to 'wait()' methods that may occur while the current thread is holding two locks. Since calling 'wait()' only releases one lock on its target, waiting with two locks held can easily lead to a deadlock. Example: 'synchronized (lockA) {\n synchronized (lockB) {\n lockB.wait(); //warning\n //thread A is stuck here holding lockA\n }\n }\n\n synchronized (lockA) { //thread B can't enter the block and release thread A\n lockB.notify();\n }'", + "markdown": "Reports calls to `wait()` methods that may occur while the current thread is holding two locks.\n\n\nSince calling `wait()` only releases one lock on its target,\nwaiting with two locks held can easily lead to a deadlock.\n\n**Example:**\n\n\n synchronized (lockA) {\n synchronized (lockB) {\n lockB.wait(); //warning\n //thread A is stuck here holding lockA\n }\n }\n\n synchronized (lockA) { //thread B can't enter the block and release thread A\n lockB.notify();\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "WaitWhileHoldingTwoLocks", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryStringEscape", + "shortDescription": { + "text": "Unnecessarily escaped character" + }, + "fullDescription": { + "text": "Reports unnecessarily escaped characters in 'String' and optionally 'char' literals. Escaped tab characters '\\t' are not reported, because tab characters are invisible. Examples: 'String s = \"\\'Scare\\' quotes\";\n String t = \"\"\"\n All you need is\\n\\tLove\\n\"\"\";' After the quick-fix is applied: 'String s = \"'Scare' quotes\";\n String t = \"\"\"\n All you need is\n \\tLove\n \"\"\";' New in 2019.3", + "markdown": "Reports unnecessarily escaped characters in `String` and optionally `char` literals.\n\nEscaped tab characters `\\t` are not reported, because tab characters are invisible.\n\nExamples:\n\n\n String s = \"\\'Scare\\' quotes\";\n String t = \"\"\"\n All you need is\\n\\tLove\\n\"\"\";\n\nAfter the quick-fix is applied:\n\n\n String s = \"'Scare' quotes\";\n String t = \"\"\"\n All you need is\n \\tLove\n \"\"\";\n\nNew in 2019.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryStringEscape", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SerializableInnerClassWithNonSerializableOuterClass", + "shortDescription": { + "text": "Serializable non-'static' inner class with non-Serializable outer class" + }, + "fullDescription": { + "text": "Reports non-static inner classes that implement 'Serializable' and are declared inside a class that doesn't implement 'Serializable'. Such classes are unlikely to serialize correctly due to implicit references to the outer class. Example: 'class A {\n class Main implements Serializable {\n }\n }' Use the following options to configure the inspection: List classes whose inheritors should not be reported by this inspection. This is meant for classes that inherit 'Serializable' from a superclass but are not intended for serialization. Whether to ignore 'Serializable' anonymous classes.", + "markdown": "Reports non-static inner classes that implement `Serializable` and are declared inside a class that doesn't implement `Serializable`.\n\n\nSuch classes are unlikely to serialize correctly due to implicit references to the outer class.\n\n**Example:**\n\n\n class A {\n class Main implements Serializable {\n }\n }\n\nUse the following options to configure the inspection:\n\n* List classes whose inheritors should not be reported by this inspection. This is meant for classes that inherit `Serializable` from a superclass but are not intended for serialization.\n* Whether to ignore `Serializable` anonymous classes." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SerializableInnerClassWithNonSerializableOuterClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JUnit5AssertionsConverter", + "shortDescription": { + "text": "JUnit 5 obsolete assertions" + }, + "fullDescription": { + "text": "Reports any calls to methods from the 'junit.framework.Assert', 'org.junit.Assert', or 'org.junit.Assume' classes inside JUnit 5 tests. Although the tests work properly, migration to 'org.junit.jupiter.api.Assertions'/'org.junit.jupiter.api.Assumptions' will help you avoid dependencies on old JUnit version. Example: 'import org.junit.Assert;\n import org.junit.jupiter.api.Test;\n\n public class MyTest {\n @Test\n public void simpleTest() {\n Assert.assertEquals(4, 2 + 2);\n }\n }' After the quick-fix is applied: 'import org.junit.jupiter.api.Assertions;\n import org.junit.jupiter.api.Test;\n\n public class MyTest {\n @Test\n public void simpleTest() {\n Assertions.assertEquals(4, 2 + 2);\n }\n }'", + "markdown": "Reports any calls to methods from the `junit.framework.Assert`, `org.junit.Assert`, or `org.junit.Assume`\nclasses inside JUnit 5 tests.\n\nAlthough the tests work properly, migration to `org.junit.jupiter.api.Assertions`/`org.junit.jupiter.api.Assumptions`\nwill help you avoid dependencies on old JUnit version.\n\n**Example:**\n\n\n import org.junit.Assert;\n import org.junit.jupiter.api.Test;\n\n public class MyTest {\n @Test\n public void simpleTest() {\n Assert.assertEquals(4, 2 + 2);\n }\n }\n\nAfter the quick-fix is applied:\n\n\n import org.junit.jupiter.api.Assertions;\n import org.junit.jupiter.api.Test;\n\n public class MyTest {\n @Test\n public void simpleTest() {\n Assertions.assertEquals(4, 2 + 2);\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "JUnit5AssertionsConverter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages/Test frameworks", + "index": 94, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspiciousListRemoveInLoop", + "shortDescription": { + "text": "Suspicious 'List.remove()' in loop" + }, + "fullDescription": { + "text": "Reports 'list.remove(index)' calls inside an ascending counted loop. This is suspicious as the list becomes shorter after the removal, and the next element gets skipped. A simple fix is to decrease the index variable after the removal, but probably removing via an iterator or using the 'removeIf()' method (Java 8 and later) is a more robust alternative. If you don't expect that 'remove()' will be called more than once in a loop, consider adding a 'break' after it. Example: 'public static void main(String[] args) {\n process(new ArrayList<>(\n Arrays.asList(\"1\", \"2\", \"|\", \"3\", \"4\")));\n }\n\n static void process(List list) {\n for (int i = 0; i < list.size(); i++) {\n if (list.get(i).equals(\"|\")) {\n list.remove(i);\n continue;\n }\n System.out.println(list.get(i));\n }\n }' The code looks like '1 2 3 4' is going to be printed, but in reality, '3' will be skipped in the output. New in 2018.2", + "markdown": "Reports `list.remove(index)` calls inside an ascending counted loop.\n\n\nThis is suspicious as the list becomes\nshorter after the removal, and the next element gets skipped. A simple fix is to decrease the index variable\nafter the removal,\nbut probably removing via an iterator or using the `removeIf()` method (Java 8 and later) is a more robust alternative.\nIf you don't expect that `remove()` will be called more than once in a loop, consider adding a `break` after it.\n\n**Example:**\n\n public static void main(String[] args) {\n process(new ArrayList<>(\n Arrays.asList(\"1\", \"2\", \"|\", \"3\", \"4\")));\n }\n\n static void process(List list) {\n for (int i = 0; i < list.size(); i++) {\n if (list.get(i).equals(\"|\")) {\n list.remove(i);\n continue;\n }\n System.out.println(list.get(i));\n }\n }\n\nThe code looks like `1 2 3 4` is going to be printed, but in reality, `3` will be skipped in the output.\n\nNew in 2018.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SuspiciousListRemoveInLoop", + "cweIds": [ + 129 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NegativeIntConstantInLongContext", + "shortDescription": { + "text": "Negative int hexadecimal constant in long context" + }, + "fullDescription": { + "text": "Reports negative int hexadecimal constants in long context. Such constants are implicitly widened to long, which means their higher bits will become 1 rather than 0 (e.g., 0xFFFF_FFFF will become 0xFFFF_FFFF_FFFF_FFFFL). Unlikely this is intended, and even if it is, using an explicit long constant would be less confusing. Example: '// Warning: this is int constant -1 which is widened to long\n // becoming 0xFFFF_FFFF_FFFF_FFFFL.\n long mask = 0xFFFF_FFFF;' New in 2022.3", + "markdown": "Reports negative int hexadecimal constants in long context. Such constants are implicitly widened to long, which means their higher bits will become 1 rather than 0 (e.g., 0xFFFF_FFFF will become 0xFFFF_FFFF_FFFF_FFFFL). Unlikely this is intended, and even if it is, using an explicit long constant would be less confusing.\n\n**Example:**\n\n\n // Warning: this is int constant -1 which is widened to long\n // becoming 0xFFFF_FFFF_FFFF_FFFFL.\n long mask = 0xFFFF_FFFF;\n\nNew in 2022.3" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "NegativeIntConstantInLongContext", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FieldCanBeLocal", + "shortDescription": { + "text": "Field can be local" + }, + "fullDescription": { + "text": "Reports redundant class fields that can be replaced with local variables. If all local usages of a field are preceded by assignments to that field, the field can be removed, and its usages can be replaced with local variables.", + "markdown": "Reports redundant class fields that can be replaced with local variables.\n\nIf all local usages of a field are preceded by assignments to that field, the\nfield can be removed, and its usages can be replaced with local variables." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "FieldCanBeLocal", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UrlHashCode", + "shortDescription": { + "text": "Call to 'equals()' or 'hashCode()' on 'URL' object" + }, + "fullDescription": { + "text": "Reports 'hashCode()' and 'equals()' calls on 'java.net.URL' objects and calls that add 'URL' objects to maps and sets. 'URL''s 'equals()' and 'hashCode()' methods can perform a DNS lookup to resolve the host name. This may cause significant delays, depending on the availability and speed of the network and the DNS server. Using 'java.net.URI' instead of 'java.net.URL' will avoid the DNS lookup. Example: 'boolean urlEquals(URL url1, URL url2) {\n return url1.equals(url2);\n }'", + "markdown": "Reports `hashCode()` and `equals()` calls on `java.net.URL` objects and calls that add `URL` objects to maps and sets.\n\n\n`URL`'s `equals()` and `hashCode()` methods can perform a DNS lookup to resolve the host name.\nThis may cause significant delays, depending on the availability and speed of the network and the DNS server.\nUsing `java.net.URI` instead of `java.net.URL` will avoid the DNS lookup.\n\n**Example:**\n\n\n boolean urlEquals(URL url1, URL url2) {\n return url1.equals(url2);\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "UrlHashCode", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages", + "index": 2, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "WhileLoopSpinsOnField", + "shortDescription": { + "text": "'while' loop spins on field" + }, + "fullDescription": { + "text": "Reports 'while' loops that spin on the value of a non-'volatile' field, waiting for it to be changed by another thread. In addition to being potentially extremely CPU intensive when little work is done inside the loop, such loops are likely to have different semantics from what was intended. The Java Memory Model allows such loops to never complete even if another thread changes the field's value. Additionally, since Java 9 it's recommended to call 'Thread.onSpinWait()' inside a spin loop on a 'volatile' field, which may significantly improve performance on some hardware. Example: 'class SpinsOnField {\n boolean ready = false;\n\n void run() {\n while (!ready) {\n }\n // do some work\n }\n\n void markAsReady() {\n ready = true;\n }\n }' After the quick-fix is applied: 'class SpinsOnField {\n volatile boolean ready = false;\n\n void run() {\n while (!ready) {\n Thread.onSpinWait();\n }\n // do some work\n }\n\n void markAsReady() {\n ready = true;\n }\n }' Use the inspection options to only report empty 'while' loops.", + "markdown": "Reports `while` loops that spin on the value of a non-`volatile` field, waiting for it to be changed by another thread.\n\n\nIn addition to being potentially extremely CPU intensive when little work is done inside the loop, such\nloops are likely to have different semantics from what was intended.\nThe Java Memory Model allows such loops to never complete even if another thread changes the field's value.\n\n\nAdditionally, since Java 9 it's recommended to call `Thread.onSpinWait()` inside a spin loop\non a `volatile` field, which may significantly improve performance on some hardware.\n\n**Example:**\n\n\n class SpinsOnField {\n boolean ready = false;\n\n void run() {\n while (!ready) {\n }\n // do some work\n }\n\n void markAsReady() {\n ready = true;\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class SpinsOnField {\n volatile boolean ready = false;\n\n void run() {\n while (!ready) {\n Thread.onSpinWait();\n }\n // do some work\n }\n\n void markAsReady() {\n ready = true;\n }\n }\n\n\nUse the inspection options to only report empty `while` loops." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "WhileLoopSpinsOnField", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DefaultAnnotationParam", + "shortDescription": { + "text": "Default annotation parameter value" + }, + "fullDescription": { + "text": "Reports annotation parameters that are assigned to their 'default' value. Example: '@interface Test {\n Class expected() default Throwable.class;\n }\n\n @Test(expected = Throwable.class)\n void testSmth() {}' After the quick-fix is applied: '@Test()\n void testSmth() {}'", + "markdown": "Reports annotation parameters that are assigned to their `default` value.\n\nExample:\n\n\n @interface Test {\n Class expected() default Throwable.class;\n }\n\n @Test(expected = Throwable.class)\n void testSmth() {}\n\nAfter the quick-fix is applied:\n\n\n @Test()\n void testSmth() {}\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "DefaultAnnotationParam", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IfStatementWithIdenticalBranches", + "shortDescription": { + "text": "'if' statement with identical branches" + }, + "fullDescription": { + "text": "Reports 'if' statements in which common parts can be extracted from the branches. These common parts are independent from the condition and make 'if' statements harder to understand. Example: 'if (x > 12) {\n doSomethingBefore();\n doSomethingDifferent1();\n doSomethingAfter();\n } else {\n doSomethingBefore();\n doSomethingDifferent2();\n doSomethingAfter();\n }' After the quick-fix is applied: 'doSomethingBefore();\n if (x > 12) {\n doSomethingDifferent1();\n } else {\n doSomethingDifferent2();\n }\n doSomethingAfter();' Updated in 2018.1", + "markdown": "Reports `if` statements in which common parts can be extracted from the branches.\n\nThese common parts are independent from the condition and make `if` statements harder to understand.\n\nExample:\n\n\n if (x > 12) {\n doSomethingBefore();\n doSomethingDifferent1();\n doSomethingAfter();\n } else {\n doSomethingBefore();\n doSomethingDifferent2();\n doSomethingAfter();\n }\n\nAfter the quick-fix is applied:\n\n\n doSomethingBefore();\n if (x > 12) {\n doSomethingDifferent1();\n } else {\n doSomethingDifferent2();\n }\n doSomethingAfter();\n\nUpdated in 2018.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "note", + "parameters": { + "suppressToolId": "IfStatementWithIdenticalBranches", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InterfaceWithOnlyOneDirectInheritor", + "shortDescription": { + "text": "Interface with a single direct inheritor" + }, + "fullDescription": { + "text": "Reports interfaces that have precisely one direct inheritor. While such interfaces may offer admirable clarity of design, in memory-constrained or bandwidth-limited environments, they needlessly increase the total footprint of the application. Consider merging the interface with its inheritor. This inspection is intended for Java ME and other highly resource constrained environments. Applying the results of this inspection without consideration might have negative effects on code clarity and design.", + "markdown": "Reports interfaces that have precisely one direct inheritor. While such interfaces may offer admirable clarity of design, in memory-constrained or bandwidth-limited environments, they needlessly increase the total footprint of the application. Consider merging the interface with its inheritor.\n\n\nThis inspection is intended for Java ME and other highly resource constrained environments.\nApplying the results of this inspection without consideration might have negative effects on code clarity and design." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "InterfaceWithOnlyOneDirectInheritor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance/Embedded", + "index": 19, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InstanceofChain", + "shortDescription": { + "text": "Chain of 'instanceof' checks" + }, + "fullDescription": { + "text": "Reports any chains of 'if'-'else' statements all of whose conditions are 'instanceof' expressions or class equality expressions (e.g. comparison with 'String.class'). Such constructions usually indicate a failure in object-oriented design which dictates that such type-based dispatch should be done via polymorphic method calls rather than explicit chains of type tests. Example: 'double getArea(Shape shape) {\n // Warning: abstraction failure.\n // It would be better to declare a getArea()\n // abstract method in the shape interface\n // and implement it in every inheritor.\n if (shape instanceof Point) {\n return 0;\n }\n if (shape instanceof Circle) {\n return Math.PI *\n Math.pow(((Circle) shape).radius(), 2);\n }\n if (shape instanceof Rectangle) {\n return ((Rectangle) shape).width() *\n ((Rectangle) shape).height();\n }\n throw new IllegalArgumentException();\n }' Use the checkbox below to ignore 'instanceof' expressions on library classes.", + "markdown": "Reports any chains of `if`-`else` statements all of whose conditions are `instanceof` expressions or class equality expressions (e.g. comparison with `String.class`). Such constructions usually indicate a failure in object-oriented design which dictates that such type-based dispatch should be done via polymorphic method calls rather than explicit chains of type tests.\n\nExample:\n\n\n double getArea(Shape shape) {\n // Warning: abstraction failure.\n // It would be better to declare a getArea()\n // abstract method in the shape interface\n // and implement it in every inheritor.\n if (shape instanceof Point) {\n return 0;\n }\n if (shape instanceof Circle) {\n return Math.PI *\n Math.pow(((Circle) shape).radius(), 2);\n }\n if (shape instanceof Rectangle) {\n return ((Rectangle) shape).width() *\n ((Rectangle) shape).height();\n }\n throw new IllegalArgumentException();\n }\n\n\nUse the checkbox below to ignore `instanceof` expressions on library classes." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ChainOfInstanceofChecks", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Abstraction issues", + "index": 78, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BlockMarkerComments", + "shortDescription": { + "text": "Block marker comment" + }, + "fullDescription": { + "text": "Reports comments which are used as code block markers. The quick-fix removes such comments. Example: 'while (i < 10) {\n i++;\n } // end while' After the quick-fix is applied: 'while (i < 10) {\n i++;\n }'", + "markdown": "Reports comments which are used as code block markers. The quick-fix removes such comments.\n\nExample:\n\n\n while (i < 10) {\n i++;\n } // end while\n\nAfter the quick-fix is applied:\n\n\n while (i < 10) {\n i++;\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "BlockMarkerComments", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SerializableHasSerializationMethods", + "shortDescription": { + "text": "Serializable class without 'readObject()' and 'writeObject()'" + }, + "fullDescription": { + "text": "Reports 'Serializable' classes that do not implement 'readObject()' and 'writeObject()' methods. If 'readObject()' and 'writeObject()' methods are not implemented, the default serialization algorithms are used, which may be sub-optimal for performance and compatibility in many environments. Use the following options to configure the inspection: List classes whose inheritors should not be reported by this inspection. This is meant for classes that inherit 'Serializable' from a superclass but are not intended for serialization. Whether to ignore 'Serializable' classes without non-static fields. Whether to ignore 'Serializable' anonymous classes.", + "markdown": "Reports `Serializable` classes that do not implement `readObject()` and `writeObject()` methods.\n\n\nIf `readObject()` and `writeObject()` methods are not implemented,\nthe default serialization algorithms are used,\nwhich may be sub-optimal for performance and compatibility in many environments.\n\n\nUse the following options to configure the inspection:\n\n* List classes whose inheritors should not be reported by this inspection. This is meant for classes that inherit `Serializable` from a superclass but are not intended for serialization.\n* Whether to ignore `Serializable` classes without non-static fields.\n* Whether to ignore `Serializable` anonymous classes." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SerializableHasSerializationMethods", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SynchronizeOnLock", + "shortDescription": { + "text": "Synchronization on a 'Lock' object" + }, + "fullDescription": { + "text": "Reports 'synchronized' blocks that lock on an instance of 'java.util.concurrent.locks.Lock'. Such synchronization is almost certainly unintended, and appropriate versions of '.lock()' and '.unlock()' should be used instead. Example: 'final ReentrantLock lock = new ReentrantLock();\n\n public void foo() {\n synchronized (lock) {}\n }'", + "markdown": "Reports `synchronized` blocks that lock on an instance of `java.util.concurrent.locks.Lock`. Such synchronization is almost certainly unintended, and appropriate versions of `.lock()` and `.unlock()` should be used instead.\n\n**Example:**\n\n\n final ReentrantLock lock = new ReentrantLock();\n\n public void foo() {\n synchronized (lock) {}\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SynchroniziationOnLockObject", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BadExceptionCaught", + "shortDescription": { + "text": "Prohibited 'Exception' caught" + }, + "fullDescription": { + "text": "Reports 'catch' clauses that catch an inappropriate exception. Some exceptions, for example 'java.lang.NullPointerException' or 'java.lang.IllegalMonitorStateException', represent programming errors and therefore almost certainly should not be caught in production code. Example: 'try {\n return component.getMousePosition(true) != null;\n } catch (NullPointerException e) { // warning: Prohibited exception 'NullPointerException' caught\n return false;\n }' Use the Prohibited exceptions list to specify which exceptions should be reported.", + "markdown": "Reports `catch` clauses that catch an inappropriate exception.\n\nSome exceptions, for example\n`java.lang.NullPointerException` or\n`java.lang.IllegalMonitorStateException`, represent programming errors\nand therefore almost certainly should not be caught in production code.\n\n**Example:**\n\n\n try {\n return component.getMousePosition(true) != null;\n } catch (NullPointerException e) { // warning: Prohibited exception 'NullPointerException' caught\n return false;\n }\n\nUse the **Prohibited exceptions** list to specify which exceptions should be reported." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ProhibitedExceptionCaught", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IdempotentLoopBody", + "shortDescription": { + "text": "Idempotent loop body" + }, + "fullDescription": { + "text": "Reports loops whose second and all subsequent iterations do not produce any additional side effects other than the one produced by the first iteration, which can indicate a programming error. Such loops may iterate only zero, one, or infinite number of times. If the infinite number of times case is unreachable, such a loop can be replaced with an 'if' statement. Otherwise, there's a possibility that the program can get stuck. Example: 'public void foo(String baseName, String names) {\n int suffix = 1;\n String name = baseName;\n while (names.contains(name)) {\n // error: suffix is not updated making loop body idempotent\n name = baseName + suffix;\n }\n }' New in 2018.1", + "markdown": "Reports loops whose second and all subsequent iterations do not produce any additional side effects other than the one produced by the first iteration, which can indicate a programming error.\n\nSuch loops may iterate only zero, one, or infinite number of times.\nIf the infinite number of times case is unreachable, such a loop can be replaced with an `if` statement.\nOtherwise, there's a possibility that the program can get stuck.\n\nExample:\n\n\n public void foo(String baseName, String names) {\n int suffix = 1;\n String name = baseName;\n while (names.contains(name)) {\n // error: suffix is not updated making loop body idempotent\n name = baseName + suffix;\n }\n }\n\nNew in 2018.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "IdempotentLoopBody", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantImplements", + "shortDescription": { + "text": "Redundant interface declaration" + }, + "fullDescription": { + "text": "Reports interfaces in a class' 'implements' list or an interface's 'extends' list that are already implemented by a superclass or extended by a superinterface. Such declarations are unnecessary and may be safely removed. Example: 'class X implements One, Two {\n }\n interface One {}\n interface Two extends One {}' After the quick-fix is applied: 'class X implements Two {\n }\n interface One {}\n interface Two extends One {}' Use the options to not report on 'Serializable' or 'Externalizable' in an 'extends' or 'implements' list.", + "markdown": "Reports interfaces in a class' `implements` list or an interface's `extends` list that are already implemented by a superclass or extended by a superinterface. Such declarations are unnecessary and may be safely removed.\n\n**Example:**\n\n\n class X implements One, Two {\n }\n interface One {}\n interface Two extends One {}\n\nAfter the quick-fix is applied:\n\n\n class X implements Two {\n }\n interface One {}\n interface Two extends One {}\n\n\nUse the options to not report on `Serializable` or `Externalizable`\nin an `extends` or `implements` list." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantInterfaceDeclaration", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Inheritance issues", + "index": 25, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InstanceofCatchParameter", + "shortDescription": { + "text": "'instanceof' on 'catch' parameter" + }, + "fullDescription": { + "text": "Reports cases in which an 'instanceof' expression is used for testing the type of a parameter in a 'catch' block. Testing the type of 'catch' parameters is usually better done by having separate 'catch' blocks instead of using 'instanceof'. Example: 'void foo(Runnable runnable) {\n try {\n runnable.run();\n } catch (Throwable throwable) {\n if (throwable instanceof NoClassDefFoundError) { // warning: 'instanceof' on 'catch' parameter 'throwable'\n System.out.println(\"Class not found!\");\n }\n }\n }'", + "markdown": "Reports cases in which an `instanceof` expression is used for testing the type of a parameter in a `catch` block.\n\nTesting the type of `catch` parameters is usually better done by having separate\n`catch` blocks instead of using `instanceof`.\n\n**Example:**\n\n\n void foo(Runnable runnable) {\n try {\n runnable.run();\n } catch (Throwable throwable) {\n if (throwable instanceof NoClassDefFoundError) { // warning: 'instanceof' on 'catch' parameter 'throwable'\n System.out.println(\"Class not found!\");\n }\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "InstanceofCatchParameter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantScheduledForRemovalAnnotation", + "shortDescription": { + "text": "Redundant @ScheduledForRemoval annotation" + }, + "fullDescription": { + "text": "Reports usages of '@ApiStatus.ScheduledForRemoval' annotation without 'inVersion' attribute in code which targets Java 9 or newer version. Such usages can be replaced by 'forRemoval' attribute in '@Deprecated' annotation to simplify code. New in 2022.1", + "markdown": "Reports usages of `@ApiStatus.ScheduledForRemoval` annotation without `inVersion` attribute in code which targets Java 9 or newer version.\n\n\nSuch usages can be replaced by `forRemoval` attribute in `@Deprecated` annotation to simplify code.\n\nNew in 2022.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantScheduledForRemovalAnnotation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code maturity", + "index": 50, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OptionalGetWithoutIsPresent", + "shortDescription": { + "text": "Optional.get() is called without isPresent() check" + }, + "fullDescription": { + "text": "Reports calls to 'get()' on an 'Optional' without checking that it has a value. Calling 'Optional.get()' on an empty 'Optional' instance will throw an exception. Example: 'void x(List list) {\n final Optional optional =\n list.stream().filter(x -> x > 10).findFirst();\n final Integer result = optional.get(); // problem here\n }'", + "markdown": "Reports calls to `get()` on an `Optional` without checking that it has a value.\n\nCalling `Optional.get()` on an empty `Optional` instance will throw an exception.\n\n**Example:**\n\n\n void x(List list) {\n final Optional optional =\n list.stream().filter(x -> x > 10).findFirst();\n final Integer result = optional.get(); // problem here\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "OptionalGetWithoutIsPresent", + "cweIds": [ + 252, + 476 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FrequentlyUsedInheritorInspection", + "shortDescription": { + "text": "Class may extend a commonly used base class" + }, + "fullDescription": { + "text": "Reports classes or interfaces that can be replaced with an implementation or extension of a more specific commonly used class or interface. For this inspection to work, a superclass needs to be in project source files and the project needs to use the IntelliJ IDEA build system. Example: 'class MyInheritor implements A {} // B suggested on the A reference\n\n interface A {}\n\n abstract class B implements A {}\n\n abstract class C1 extends B {}\n abstract class C2 extends B {}\n abstract class C3 extends B {}\n abstract class C4 extends B {}\n abstract class C5 extends B {}' By default, this inspection doesn't highlight issues in the editor but only provides a quick-fix. New in 2017.2", + "markdown": "Reports classes or interfaces that can be replaced with an implementation or extension of a more specific commonly used class or interface.\n\nFor this inspection to work, a superclass needs to be in project source files and the project needs to use the IntelliJ IDEA build system.\n\n**Example:**\n\n\n class MyInheritor implements A {} // B suggested on the A reference\n\n interface A {}\n\n abstract class B implements A {}\n\n abstract class C1 extends B {}\n abstract class C2 extends B {}\n abstract class C3 extends B {}\n abstract class C4 extends B {}\n abstract class C5 extends B {}\n\nBy default, this inspection doesn't highlight issues in the editor but only provides a quick-fix.\n\nNew in 2017.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "FrequentlyUsedInheritorInspection", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Inheritance issues", + "index": 25, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OnDemandImport", + "shortDescription": { + "text": "'*' import" + }, + "fullDescription": { + "text": "Reports any 'import' statements that cover entire packages ('* imports'). Some coding standards prohibit such 'import' statements. You can configure IntelliJ IDEA to detect and fix such statements with its Optimize Imports command. Go to Settings | Editor | Code Style | Java | Imports, make sure that the Use single class import option is enabled, and specify values in the Class count to use import with '*' and Names count to use static import with '*' fields. Thus this inspection is mostly useful for offline reporting on code bases that you don't intend to change.", + "markdown": "Reports any `import` statements that cover entire packages ('\\* imports').\n\nSome coding standards prohibit such `import` statements.\n\n\nYou can configure IntelliJ IDEA to detect and fix such statements with its **Optimize Imports**\ncommand. Go to [Settings \\| Editor \\| Code Style \\| Java \\| Imports](settings://preferences.sourceCode.Java?Use%20single%20class%20import),\nmake sure that the **Use single class import** option is enabled, and specify values in the\n**Class count to use import with '\\*'** and **Names count to use static import with '\\*'** fields.\nThus this inspection is mostly useful for offline reporting on code bases that you don't\nintend to change." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OnDemandImport", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Imports", + "index": 22, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FallthruInSwitchStatement", + "shortDescription": { + "text": "Fallthrough in 'switch' statement" + }, + "fullDescription": { + "text": "Reports 'fall-through' in a 'switch' statement. Fall-through occurs when a series of executable statements after a 'case' label is not guaranteed to transfer control before the next 'case' label. For example, this can happen if the branch is missing a 'break' statement. In that case, control falls through to the statements after that 'switch' label, even though the 'switch' expression is not equal to the value of the fallen-through label. While occasionally intended, this construction is confusing and is often the result of a typo. This inspection ignores any fall-through commented with a text matching the regex pattern '(?i)falls?\\s*thro?u'. There is a fix that adds a 'break' to the branch that can fall through to the next branch. Example: 'switch(x) {\n case (4):\n if (condition) {\n System.out.println(\"3\");\n // no break here\n } else {\n break;\n }\n case (6):\n System.out.println(\"4\");\n }' After the quick-fix is applied: 'switch(x) {\n case (4):\n if (condition) {\n System.out.println(\"3\");\n } else {\n break;\n }\n break;\n case (6):\n System.out.println(\"4\");\n }'", + "markdown": "Reports 'fall-through' in a `switch` statement.\n\nFall-through occurs when a series of executable statements after a `case` label is not guaranteed\nto transfer control before the next `case` label. For example, this can happen if the branch is missing a `break` statement.\nIn that case, control falls through to the statements after\nthat `switch` label, even though the `switch` expression is not equal to\nthe value of the fallen-through label. While occasionally intended, this construction is confusing and is often the result of a typo.\n\n\nThis inspection ignores any fall-through commented with a text matching the regex pattern `(?i)falls?\\s*thro?u`.\n\nThere is a fix that adds a `break` to the branch that can fall through to the next branch.\n\nExample:\n\n\n switch(x) {\n case (4):\n if (condition) {\n System.out.println(\"3\");\n // no break here\n } else {\n break;\n }\n case (6):\n System.out.println(\"4\");\n }\n\nAfter the quick-fix is applied:\n\n\n switch(x) {\n case (4):\n if (condition) {\n System.out.println(\"3\");\n } else {\n break;\n }\n break;\n case (6):\n System.out.println(\"4\");\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "fallthrough", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OptionalIsPresent", + "shortDescription": { + "text": "Non functional style 'Optional.isPresent()' usage" + }, + "fullDescription": { + "text": "Reports 'Optional' expressions used as 'if' or conditional expression conditions, that can be rewritten in a functional style. The result is often shorter and easier to read. Example: 'if (str.isPresent()) str.get().trim();' After the quick-fix is applied: 'str.ifPresent(String::trim);' This inspection only reports if the language level of the project or module is 8 or higher.", + "markdown": "Reports `Optional` expressions used as `if` or conditional expression conditions, that can be rewritten in a functional style. The result is often shorter and easier to read.\n\nExample:\n\n\n if (str.isPresent()) str.get().trim();\n\nAfter the quick-fix is applied:\n\n\n str.ifPresent(String::trim);\n\nThis inspection only reports if the language level of the project or module is 8 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OptionalIsPresent", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantOperationOnEmptyContainer", + "shortDescription": { + "text": "Redundant operation on empty container" + }, + "fullDescription": { + "text": "Reports redundant operations on empty collections, maps or arrays. Iterating, removing elements, sorting, and some other operations on empty collections have no effect and can be removed. Also, they may be a signal of a bug. Example: 'if (numbers.isEmpty()){\n //error due to the missed negation\n int max = numbers.stream().max(Comparator.naturalOrder()).get();\n ...\n }' New in 2019.1", + "markdown": "Reports redundant operations on empty collections, maps or arrays.\n\n\nIterating, removing elements, sorting,\nand some other operations on empty collections have no effect and can be removed. Also, they may be a signal of a bug.\n\n**Example:**\n\n\n if (numbers.isEmpty()){\n //error due to the missed negation\n int max = numbers.stream().max(Comparator.naturalOrder()).get();\n ...\n }\n\nNew in 2019.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantOperationOnEmptyContainer", + "cweIds": [ + 561 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AtomicFieldUpdaterNotStaticFinal", + "shortDescription": { + "text": "'AtomicFieldUpdater' field not declared 'static final'" + }, + "fullDescription": { + "text": "Reports fields of types: 'java.util.concurrent.atomic.AtomicLongFieldUpdater' 'java.util.concurrent.atomic.AtomicIntegerFieldUpdater' 'java.util.concurrent.atomic.AtomicReferenceFieldUpdater' that are not 'static final'. Because only one atomic field updater is needed for updating a 'volatile' field in all instances of a class, it can almost always be 'static'. Making the updater 'final' allows the JVM to optimize access for improved performance. Example: 'class Main {\n private volatile int id;\n private AtomicIntegerFieldUpdater

idFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(Main.class, \"id\");\n }' After the quick-fix is applied: 'class Main {\n private volatile int id;\n private static final AtomicIntegerFieldUpdater
idFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(Main.class, \"id\");\n }'", + "markdown": "Reports fields of types:\n\n* `java.util.concurrent.atomic.AtomicLongFieldUpdater`\n* `java.util.concurrent.atomic.AtomicIntegerFieldUpdater`\n* `java.util.concurrent.atomic.AtomicReferenceFieldUpdater`\n\nthat are not `static final`. Because only one atomic field updater is needed for updating a `volatile` field in all instances of a class, it can almost always be `static`.\n\nMaking the updater `final` allows the JVM to optimize access for improved performance.\n\n**Example:**\n\n\n class Main {\n private volatile int id;\n private AtomicIntegerFieldUpdater
idFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(Main.class, \"id\");\n }\n\nAfter the quick-fix is applied:\n\n\n class Main {\n private volatile int id;\n private static final AtomicIntegerFieldUpdater
idFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(Main.class, \"id\");\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "AtomicFieldUpdaterNotStaticFinal", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FieldNamingConvention", + "shortDescription": { + "text": "Field naming convention" + }, + "fullDescription": { + "text": "Reports fields whose names are too short, too long, or do not follow the specified regular expression pattern. Example: if the inspection is enabled for constants, and the minimum specified length for a field name is 5 (the default), the following constant produces a warning because the length of its name is 3, which is less than 5: 'public static final int MAX = 42;'. A quick-fix that renames such fields is available only in the editor. Configure the inspection: Use the list in the Options section to specify which fields should be checked. Deselect the checkboxes for the fields for which you want to skip the check. For each field type, specify the minimum length, maximum length, and the regular expression expected for field names using the provided input fields. Specify 0 in the length fields to skip the corresponding checks. Regular expressions should be specified in the standard 'java.util.regex' format.", + "markdown": "Reports fields whose names are too short, too long, or do not follow the specified regular expression pattern.\n\n**Example:** if the inspection is enabled for constants, and the minimum specified length for a field name is 5 (the default), the following constant\nproduces a warning because the length of its name is 3, which is less than 5: `public static final int MAX = 42;`.\n\nA quick-fix that renames such fields is available only in the editor.\n\nConfigure the inspection:\n\nUse the list in the **Options** section to specify which fields should be checked. Deselect the checkboxes for the fields for which\nyou want to skip the check.\n\nFor each field type, specify the minimum length, maximum length, and the regular expression expected for field names using the\nprovided input fields.\nSpecify **0** in the length fields to skip the corresponding checks.\n\nRegular expressions should be specified in the standard\n`java.util.regex` format." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "FieldNamingConvention", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions", + "index": 51, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java8MapForEach", + "shortDescription": { + "text": "Map.forEach() can be used" + }, + "fullDescription": { + "text": "Suggests replacing 'for(Entry entry : map.entrySet()) {...}' or 'map.entrySet().forEach(entry -> ...)' with 'map.forEach((key, value) -> ...)'. Example 'void print(Map map) {\n map.entrySet().forEach(entry -> {\n String str = entry.getKey();\n System.out.println(str + \":\" + entry.getValue());\n });\n }' After the quick-fix is applied: 'void print(Map map) {\n map.forEach((str, value) -> System.out.println(str + \":\" + value));\n }' When the Do not report loops option is enabled, only 'entrySet().forEach()' cases will be reported. However, the quick-fix action will be available for 'for'-loops as well. This inspection only reports if the language level of the project or module is 8 or higher. New in 2017.1", + "markdown": "Suggests replacing `for(Entry entry : map.entrySet()) {...}` or `map.entrySet().forEach(entry -> ...)` with `map.forEach((key, value) -> ...)`.\n\nExample\n\n\n void print(Map map) {\n map.entrySet().forEach(entry -> {\n String str = entry.getKey();\n System.out.println(str + \":\" + entry.getValue());\n });\n }\n\nAfter the quick-fix is applied:\n\n\n void print(Map map) {\n map.forEach((str, value) -> System.out.println(str + \":\" + value));\n }\n\n\nWhen the **Do not report loops** option is enabled, only `entrySet().forEach()` cases will be reported.\nHowever, the quick-fix action will be available for `for`-loops as well.\n\nThis inspection only reports if the language level of the project or module is 8 or higher.\n\nNew in 2017.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "Java8MapForEach", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 8", + "index": 62, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConstantConditionalExpression", + "shortDescription": { + "text": "Constant conditional expression" + }, + "fullDescription": { + "text": "Reports conditional expressions in which the condition is either a 'true' or 'false' constant. These expressions sometimes occur as a result of automatic refactorings and may be simplified. Example: 'return true ? \"Yes\" : \"No\";' After quick-fix is applied: 'return \"Yes\";'", + "markdown": "Reports conditional expressions in which the condition is either a `true` or `false` constant. These expressions sometimes occur as a result of automatic refactorings and may be simplified.\n\nExample:\n\n\n return true ? \"Yes\" : \"No\";\n\nAfter quick-fix is applied:\n\n\n return \"Yes\";\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ConstantConditionalExpression", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassNamePrefixedWithPackageName", + "shortDescription": { + "text": "Class name prefixed with package name" + }, + "fullDescription": { + "text": "Reports classes whose names are prefixed with their package names, ignoring differences in capitalization. While occasionally having such names is reasonable, they are often used due to a poor naming scheme, may be redundant and annoying. Example: 'package byteCode;\n class ByteCodeAnalyzer {}' A quick-fix that renames such classes is available only in the editor.", + "markdown": "Reports classes whose names are prefixed with their package names, ignoring differences in capitalization.\n\nWhile occasionally having such names is reasonable, they are often used due to a poor naming scheme, may be redundant and\nannoying.\n\n**Example:**\n\n\n package byteCode;\n class ByteCodeAnalyzer {}\n\nA quick-fix that renames such classes is available only in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassNamePrefixedWithPackageName", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions/Class", + "index": 71, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SameParameterValue", + "shortDescription": { + "text": "Method parameter always has the same value" + }, + "fullDescription": { + "text": "Reports method parameters that always have the same constant value. Example: 'static void printPoint(int x, int y) { // x is always 0\n System.out.println(x + \", \" + y);\n }\n\n public static void main(String[] args) {\n printPoint(0, 1);\n printPoint(0, 2);\n }' The quick-fix inlines the constant value. This may simplify the method implementation. Use the Ignore when a quick-fix can not be provided option to suppress the inspections when: the parameter is modified inside the method the parameter value that is being passed is a reference to an inaccessible field (Java ony) the parameter is a vararg (Java only) Use the Maximal method visibility option to control the maximum visibility of methods to be reported. Use the Minimal method usage count to report parameter field to specify the minimal number of method usages with the same parameter value.", + "markdown": "Reports method parameters that always have the same constant value.\n\nExample:\n\n\n static void printPoint(int x, int y) { // x is always 0\n System.out.println(x + \", \" + y);\n }\n\n public static void main(String[] args) {\n printPoint(0, 1);\n printPoint(0, 2);\n }\n\nThe quick-fix inlines the constant value. This may simplify the method implementation.\n\n\nUse the **Ignore when a quick-fix can not be provided** option to suppress the inspections when:\n\n* the parameter is modified inside the method\n* the parameter value that is being passed is a reference to an inaccessible field (Java ony)\n* the parameter is a vararg (Java only)\n\n\nUse the **Maximal method visibility** option to control the maximum visibility of methods to be reported.\n\n\nUse the **Minimal method usage count to report parameter** field to specify the minimal number of method usages with the same parameter value." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SameParameterValue", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CatchMayIgnoreException", + "shortDescription": { + "text": "Catch block may ignore exception" + }, + "fullDescription": { + "text": "Reports 'catch' blocks that are empty or may ignore an exception. While occasionally intended, empty 'catch' blocks may complicate debugging. Also, ignoring a 'catch' parameter might be wrong. Finally, the static code analyzer reports if it detects that a 'catch' block may silently ignore important VM exceptions like 'NullPointerException'. Ignoring such an exception (without logging or rethrowing it) may hide a bug. The inspection won't report any 'catch' parameters named 'ignore' or 'ignored'. Conversely, the inspection will warn you about any 'catch' parameters named 'ignore' or 'ignored' that are actually in use. Additionally, the inspection won't report 'catch' parameters inside test sources named 'expected' or 'ok'. You can use a quick-fix to change the exception name to 'ignored'. For empty catch blocks, an additional quick-fix to generate the catch body is suggested. You can modify the \"Catch Statement Body\" template on the Code tab in Settings | Editor | File and Code Templates. Example: 'try {\n throwingMethod();\n } catch (IOException ex) {\n\n }' After the quick-fix is applied: 'try {\n System.out.println(System.in.read());\n } catch (IOException ignored) {\n\n }' Configure the inspection: Use the Do not warn when 'catch' block contains a comment option to ignore 'catch' blocks with comments. Use the Do not warn when 'catch' block is not empty option to ignore 'catch' blocks that contain statements or comments inside, while the variable itself is not used. Use the Do not warn when exception named 'ignore(d)' is not actually ignored option to ignore variables named 'ignored' if they are in use. New in 2018.1", + "markdown": "Reports `catch` blocks that are empty or may ignore an exception.\n\nWhile occasionally intended, empty `catch` blocks may complicate debugging.\nAlso, ignoring a `catch` parameter might be wrong.\nFinally, the static code analyzer reports if it detects that a `catch` block may silently ignore important VM\nexceptions like `NullPointerException`. Ignoring such an exception\n(without logging or rethrowing it) may hide a bug.\n\n\nThe inspection won't report any `catch` parameters named `ignore` or `ignored`.\nConversely, the inspection will warn you about any `catch` parameters named `ignore` or `ignored` that are actually in use.\nAdditionally, the inspection won't report `catch` parameters inside test sources named `expected` or `ok`.\n\n\nYou can use a quick-fix to change the exception name to `ignored`.\nFor empty **catch** blocks, an additional quick-fix to generate the **catch** body is suggested.\nYou can modify the \"Catch Statement Body\" template on the Code tab in\n[Settings \\| Editor \\| File and Code Templates](settings://fileTemplates).\n\n**Example:**\n\n\n try {\n throwingMethod();\n } catch (IOException ex) {\n\n }\n\nAfter the quick-fix is applied:\n\n\n try {\n System.out.println(System.in.read());\n } catch (IOException ignored) {\n\n }\n\nConfigure the inspection:\n\n* Use the **Do not warn when 'catch' block contains a comment** option to ignore `catch` blocks with comments.\n* Use the **Do not warn when 'catch' block is not empty** option to ignore `catch` blocks that contain statements or comments inside, while the variable itself is not used.\n* Use the **Do not warn when exception named 'ignore(d)' is not actually ignored** option to ignore variables named `ignored` if they are in use.\n\nNew in 2018.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "CatchMayIgnoreException", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RecordStoreResource", + "shortDescription": { + "text": "'RecordStore' opened but not safely closed" + }, + "fullDescription": { + "text": "Reports Java ME 'javax.microedition.rms.RecordStore' resources that are not opened in front of a 'try' block and closed in the corresponding 'finally' block. Such resources may be inadvertently leaked if an exception is thrown before the resource is closed. This inspection is intended for Java ME and other highly resource constrained environments. Applying the results of this inspection without consideration might have negative effects on code clarity and design. Example: 'void foo1() throws RecordStoreException {\n RecordStore rs = RecordStore.openRecordStore(\"bar\", true); // warning\n }\n void foo2() throws RecordStoreException {\n RecordStore rs = RecordStore.openRecordStore(\"bar\", true); // no warning\n try {\n /* ... */\n } finally {\n rs.closeRecordStore();\n }\n }'", + "markdown": "Reports Java ME `javax.microedition.rms.RecordStore` resources that are not opened in front of a `try` block and closed in the corresponding `finally` block.\n\nSuch resources may be inadvertently leaked if an exception is thrown before the resource is closed.\n\n\nThis inspection is intended for Java ME and other highly resource constrained environments.\nApplying the results of this inspection without consideration might have negative effects on code clarity and design.\n\n**Example:**\n\n\n void foo1() throws RecordStoreException {\n RecordStore rs = RecordStore.openRecordStore(\"bar\", true); // warning\n }\n void foo2() throws RecordStoreException {\n RecordStore rs = RecordStore.openRecordStore(\"bar\", true); // no warning\n try {\n /* ... */\n } finally {\n rs.closeRecordStore();\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RecordStoreOpenedButNotSafelyClosed", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance/Embedded", + "index": 19, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassWithTooManyDependents", + "shortDescription": { + "text": "Class with too many dependents" + }, + "fullDescription": { + "text": "Reports a class on which too many other classes are directly dependent. Any modification to such a class may require changing many other classes, which may be expensive. Only top-level classes are reported. Use the field below to specify the maximum allowed number of dependents for a class. Available only from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name and isn't reported in the editor.", + "markdown": "Reports a class on which too many other classes are directly dependent.\n\nAny modification to such a class may require changing many other classes, which may be expensive.\n\nOnly top-level classes are reported.\n\nUse the field below to specify the maximum allowed number of dependents for a class.\n\nAvailable only from **Code \\| Inspect Code** or\n**Code \\| Analyze Code \\| Run Inspection by Name** and isn't reported in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassWithTooManyDependents", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Dependency issues", + "index": 89, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ObjectsEqualsCanBeSimplified", + "shortDescription": { + "text": "'Objects.equals()' can be replaced with 'equals()'" + }, + "fullDescription": { + "text": "Reports calls to 'Objects.equals(a, b)' in which the first argument is statically known to be non-null. Such a call can be safely replaced with 'a.equals(b)' or 'a == b' if both arguments are primitives. Example: 'String defaultName = \"default\";\n boolean isDefault = Objects.equals(defaultName, name);' After the quick-fix is applied: 'String defaultName = \"default\";\n boolean isDefault = defaultName.equals(name);' New in 2018.3", + "markdown": "Reports calls to `Objects.equals(a, b)` in which the first argument is statically known to be non-null.\n\nSuch a call can be safely replaced with `a.equals(b)` or `a == b` if both arguments are primitives.\n\nExample:\n\n\n String defaultName = \"default\";\n boolean isDefault = Objects.equals(defaultName, name);\n\nAfter the quick-fix is applied:\n\n\n String defaultName = \"default\";\n boolean isDefault = defaultName.equals(name);\n\nNew in 2018.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ObjectsEqualsCanBeSimplified", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "WrongPackageStatement", + "shortDescription": { + "text": "Wrong package statement" + }, + "fullDescription": { + "text": "Detects 'package' statements that do not correspond to the project directory structure. Also, reports classes without 'package' statements if the class is not located directly in source root directory. While it's not strictly mandated by Java language, it's good to keep classes from package 'com.example.myapp' inside the 'com/example/myapp' directory under the source root. Failure to do this may confuse code readers and make some tools working incorrectly.", + "markdown": "Detects `package` statements that do not correspond to the project directory structure. Also, reports classes without `package` statements if the class is not located directly in source root directory.\n\nWhile it's not strictly mandated by Java language, it's good to keep classes\nfrom package `com.example.myapp` inside the `com/example/myapp` directory under\nthe source root. Failure to do this may confuse code readers and make some tools working incorrectly." + }, + "defaultConfiguration": { + "enabled": true, + "level": "error", + "parameters": { + "suppressToolId": "WrongPackageStatement", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassLoaderInstantiation", + "shortDescription": { + "text": "'ClassLoader' instantiation" + }, + "fullDescription": { + "text": "Reports instantiations of the 'java.lang.ClassLoader' class. While often benign, any instantiations of 'ClassLoader' should be closely examined in any security audit. Example: 'Class loadExtraClass(String name) throws Exception {\n try(URLClassLoader loader =\n new URLClassLoader(new URL[]{new URL(\"extraClasses/\")})) {\n return loader.loadClass(name);\n }\n }'", + "markdown": "Reports instantiations of the `java.lang.ClassLoader` class.\n\nWhile often benign, any instantiations of `ClassLoader` should be closely examined in any security audit.\n\n**Example:**\n\n Class loadExtraClass(String name) throws Exception {\n try(URLClassLoader loader =\n new URLClassLoader(new URL[]{new URL(\"extraClasses/\")})) {\n return loader.loadClass(name);\n }\n }\n \n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassLoaderInstantiation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Security", + "index": 30, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FieldNotUsedInToString", + "shortDescription": { + "text": "Field not used in 'toString()' method" + }, + "fullDescription": { + "text": "Reports any fields that are not used in the 'toString()' method of a class. This inspection can help discover the fields that were added after the 'toString()' method was created and for which the 'toString()' method was not updated. The quick-fix regenerates the 'toString()' method. In the Generate | toString() dialog, it is possible to exclude fields from this check. This inspection will also check for problems with getter methods if the Enable getters in code generation option is enabled there. Example: 'public class Relevant {\n private String name; // not used in toString()\n private int index;\n private int length;\n\n @Override\n public String toString() {\n return \"Relevant{\" + \"index=\" + index +\n \", length=\" + length + '}';\n }\n }' After the quick-fix is applied: 'public class Relevant {\n private String name;\n private int index;\n private int length;\n\n @Override\n public String toString() {\n return \"Relevant{\" + \"name='\" + name + '\\'' +\n \", index=\" + index + \", length=\" + length + '}';\n }\n }'", + "markdown": "Reports any fields that are not used in the `toString()` method of a class.\n\nThis inspection can help discover the\nfields that were added after the `toString()` method was created and for which the `toString()` method was not\nupdated. The quick-fix regenerates the `toString()` method.\n\n\nIn the **Generate \\| toString()** dialog, it is possible to exclude fields from this check.\nThis inspection will also check for problems with getter methods if the *Enable getters in code generation* option is enabled there.\n\nExample:\n\n\n public class Relevant {\n private String name; // not used in toString()\n private int index;\n private int length;\n\n @Override\n public String toString() {\n return \"Relevant{\" + \"index=\" + index +\n \", length=\" + length + '}';\n }\n }\n\nAfter the quick-fix is applied:\n\n\n public class Relevant {\n private String name;\n private int index;\n private int length;\n\n @Override\n public String toString() {\n return \"Relevant{\" + \"name='\" + name + '\\'' +\n \", index=\" + index + \", length=\" + length + '}';\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "FieldNotUsedInToString", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/toString() issues", + "index": 118, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnresolvedClassReferenceRepair", + "shortDescription": { + "text": "Unresolved class reference" + }, + "fullDescription": { + "text": "Reports an unresolved class reference. The quick-fix suggests trying to resolve reference.", + "markdown": "Reports an unresolved class reference.\n\nThe quick-fix suggests trying to resolve reference." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "UnresolvedClassReferenceRepair", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Abstraction issues", + "index": 78, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CovariantEquals", + "shortDescription": { + "text": "Covariant 'equals()'" + }, + "fullDescription": { + "text": "Reports 'equals()' methods taking an argument type other than 'java.lang.Object' if the containing class does not have other overloads of 'equals()' that take 'java.lang.Object' as its argument type. A covariant version of 'equals()' does not override the 'Object.equals(Object)' method. It may cause unexpected behavior at runtime. For example, if the class is used to construct one of the standard collection classes, which expect that the 'Object.equals(Object)' method is overridden. Example: 'class Foo {\n public boolean equals(Foo foo) { // warning\n return false;\n }\n }\n class Bar {\n public boolean equals(Bar bar) { // no warning here\n return false;\n }\n @Override\n public boolean equals(Object obj) {\n return false;\n }\n }'", + "markdown": "Reports `equals()` methods taking an argument type other than `java.lang.Object` if the containing class does not have other overloads of `equals()` that take `java.lang.Object` as its argument type.\n\n\nA covariant version of `equals()` does not override the\n`Object.equals(Object)` method. It may cause unexpected\nbehavior at runtime. For example, if the class is used to construct\none of the standard collection classes, which expect that the\n`Object.equals(Object)` method is overridden.\n\n**Example:**\n\n\n class Foo {\n public boolean equals(Foo foo) { // warning\n return false;\n }\n }\n class Bar {\n public boolean equals(Bar bar) { // no warning here\n return false;\n }\n @Override\n public boolean equals(Object obj) {\n return false;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CovariantEquals", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Convert2Lambda", + "shortDescription": { + "text": "Anonymous type can be replaced with lambda" + }, + "fullDescription": { + "text": "Reports anonymous classes which can be replaced with lambda expressions. Example: 'new Thread(new Runnable() {\n @Override\n public void run() {\n // run thread\n }\n });' After the quick-fix is applied: 'new Thread(() -> {\n // run thread\n });' Note that if an anonymous class is converted into a stateless lambda, the same lambda object can be reused by Java runtime during subsequent invocations. On the other hand, when an anonymous class is used, separate objects are created every time. Thus, applying the quick-fix can cause the semantics change in rare cases, e.g. when anonymous class instances are used as 'HashMap' keys. Lambda syntax is not supported in Java 1.7 and earlier JVMs. Use the Report when interface is not annotated with @FunctionalInterface option to ignore the cases in which an anonymous class implements an interface without '@FunctionalInterface' annotation.", + "markdown": "Reports anonymous classes which can be replaced with lambda expressions.\n\nExample:\n\n\n new Thread(new Runnable() {\n @Override\n public void run() {\n // run thread\n }\n });\n\nAfter the quick-fix is applied:\n\n\n new Thread(() -> {\n // run thread\n });\n\n\nNote that if an anonymous class is converted into a stateless lambda, the same lambda object\ncan be reused by Java runtime during subsequent invocations. On the other hand, when an anonymous class is used,\nseparate objects are created every time. Thus, applying the quick-fix can cause the semantics change in rare cases,\ne.g. when anonymous class instances are used as `HashMap` keys.\n\nLambda syntax is not supported in Java 1.7 and earlier JVMs.\n\n\nUse the **Report when interface is not annotated with @FunctionalInterface** option to ignore the cases in which an anonymous\nclass implements an interface without `@FunctionalInterface` annotation." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "Convert2Lambda", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 8", + "index": 62, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NativeMethods", + "shortDescription": { + "text": "Native method" + }, + "fullDescription": { + "text": "Reports methods declared 'native'. Native methods are inherently unportable.", + "markdown": "Reports methods declared `native`. Native methods are inherently unportable." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NativeMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Portability", + "index": 7, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ParameterizedParametersStaticCollection", + "shortDescription": { + "text": "Parameterized test class without data provider method" + }, + "fullDescription": { + "text": "Reports JUnit 4 parameterized test classes that are annotated with '@RunWith(Parameterized.class)' but either do not include a data provider method annotated with '@Parameterized.Parameters' or this method has an incorrect signature. Such test classes cannot be run. The data provider method should be 'public' and 'static' and have a return type of 'Iterable' or 'Object[]'. Suggests creating an empty parameter provider method or changing the signature of the incorrect data provider method. Example: '@RunWith(Parameterized.class)\n public class ImportantTest {\n private int input;\n private int expected;\n\n ImportantTest(int input, int expected) {\n this.input = input;\n this.expected = expected;\n }\n\n // ... test cases\n }' After the quick-fix is applied: '@RunWith(Parameterized.class)\n public class ImportantTest {\n private int input;\n private int expected;\n\n ImportantTest(int input, int expected) {\n this.input = input;\n this.expected = expected;\n }\n\n @Parameters\n public static Iterable parameters() {\n return null;\n }\n\n // ... test cases\n }'", + "markdown": "Reports JUnit 4 [parameterized test](https://github.com/junit-team/junit4/wiki/parameterized-tests) classes that are annotated with `@RunWith(Parameterized.class)` but either do not include a data provider method annotated with `@Parameterized.Parameters` or this method has an incorrect signature. Such test classes cannot be run. The data provider method should be `public` and `static` and have a return type of `Iterable` or `Object[]`.\n\nSuggests creating an empty parameter provider method or changing the signature of the incorrect data provider method.\n\n**Example:**\n\n\n\n @RunWith(Parameterized.class)\n public class ImportantTest {\n private int input;\n private int expected;\n\n ImportantTest(int input, int expected) {\n this.input = input;\n this.expected = expected;\n }\n\n // ... test cases\n }\n\nAfter the quick-fix is applied:\n\n\n @RunWith(Parameterized.class)\n public class ImportantTest {\n private int input;\n private int expected;\n\n ImportantTest(int input, int expected) {\n this.input = input;\n this.expected = expected;\n }\n\n @Parameters\n public static Iterable parameters() {\n return null;\n }\n\n // ... test cases\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ParameterizedParametersStaticCollection", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/JUnit", + "index": 77, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SimplifiableIfStatement", + "shortDescription": { + "text": "'if' statement can be replaced with conditional or boolean expression" + }, + "fullDescription": { + "text": "Reports 'if' statements that can be replaced with conditions using the '&&', '||', '==', '!=', or '?:' operator. The result is usually shorter, but not always clearer, so it's not advised to apply the fix in every case. Example: 'if (condition) return true; else return foo;' After the quick-fix is applied: 'return condition || foo;' Configure the inspection: Use the Don't suggest '?:' operator option to disable the warning when the '?:' operator is suggested. In this case, only '&&', '||', '==', and '!=' suggestions will be highlighted. The quick-fix will still be available in the editor. Use the Ignore chained 'if' statements option to disable the warning for 'if-else' chains. The quick-fix will still be available in the editor. New in 2018.2", + "markdown": "Reports `if` statements that can be replaced with conditions using the `&&`, `||`, `==`, `!=`, or `?:` operator.\n\nThe result is usually shorter, but not always clearer, so it's not advised to apply the fix in every case.\n\nExample:\n\n\n if (condition) return true; else return foo;\n\nAfter the quick-fix is applied:\n\n\n return condition || foo;\n\nConfigure the inspection:\n\n* Use the **Don't suggest '?:' operator** option to disable the warning when the `?:` operator is suggested. In this case, only `&&`, `||`, `==`, and `!=` suggestions will be highlighted. The quick-fix will still be available in the editor.\n* Use the **Ignore chained 'if' statements** option to disable the warning for `if-else` chains. The quick-fix will still be available in the editor.\n\nNew in 2018.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "SimplifiableIfStatement", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EqualsWithItself", + "shortDescription": { + "text": "'equals()' called on itself" + }, + "fullDescription": { + "text": "Reports calls to 'equals()', 'compareTo()' or similar, that compare an object for equality with itself. The method contracts of these methods specify that such calls will always return 'true' for 'equals()' or '0' for 'compareTo()'. The inspection also checks calls to 'Objects.equals()', 'Objects.deepEquals()', 'Arrays.equals()', 'Comparator.compare()', 'assertEquals()' methods of test frameworks (JUnit, TestNG, AssertJ), 'Integer.compare()', 'Integer.compareUnsigned()' and similar methods. Example: 'class Foo {\n boolean foo(Object o) {\n return o.equals(o); // warning\n }\n\n boolean bar(String[] ss) {\n return Arrays.equals(ss, ss); // warning\n }\n}' Use the option to report test assertions report only on non-extendable library classes (like 'String') and primitive types. This option can be useful, when testing 'equals()' methods.", + "markdown": "Reports calls to `equals()`, `compareTo()` or similar, that compare an object for equality with itself. The method contracts of these methods specify that such calls will always return `true` for `equals()` or `0` for `compareTo()`. The inspection also checks calls to `Objects.equals()`, `Objects.deepEquals()`, `Arrays.equals()`, `Comparator.compare()`, `assertEquals()` methods of test frameworks (JUnit, TestNG, AssertJ), `Integer.compare()`, `Integer.compareUnsigned()` and similar methods.\n\n**Example:**\n\n\n class Foo {\n boolean foo(Object o) {\n return o.equals(o); // warning\n }\n\n boolean bar(String[] ss) {\n return Arrays.equals(ss, ss); // warning\n }\n }\n\n\nUse the option to report test assertions report only on non-extendable library classes (like `String`) and primitive types.\nThis option can be useful, when testing `equals()` methods." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "EqualsWithItself", + "cweIds": [ + 571 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassInheritanceDepth", + "shortDescription": { + "text": "Class too deep in inheritance tree" + }, + "fullDescription": { + "text": "Reports classes that are too deep in the inheritance hierarchy. Classes that are too deeply inherited may be confusing and indicate that a refactoring is necessary. All superclasses from a library are treated as a single superclass, libraries are considered unmodifiable. Use the Inheritance depth limit field to specify the maximum inheritance depth for a class.", + "markdown": "Reports classes that are too deep in the inheritance hierarchy.\n\nClasses that are too deeply inherited may be confusing and indicate that a refactoring is necessary.\n\nAll superclasses from a library are treated as a single superclass, libraries are considered unmodifiable.\n\nUse the **Inheritance depth limit** field to specify the maximum inheritance depth for a class." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassTooDeepInInheritanceTree", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class metrics", + "index": 87, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ThrowCaughtLocally", + "shortDescription": { + "text": "'throw' caught by containing 'try' statement" + }, + "fullDescription": { + "text": "Reports 'throw' statements whose exceptions are always caught by containing 'try' statements. Using 'throw' statements as a \"goto\" to change the local flow of control is confusing and results in poor performance. Example: 'try {\n if (!Files.isDirectory(PROJECTS)) {\n throw new IllegalStateException(\"Directory not found.\"); // warning: 'throw' caught by containing 'try' statement\n }\n ...\n } catch (Exception e) {\n LOG.error(\"run failed\");\n }' Use the Ignore rethrown exceptions option to ignore exceptions that are rethrown.", + "markdown": "Reports `throw` statements whose exceptions are always caught by containing `try` statements.\n\nUsing `throw`\nstatements as a \"goto\" to change the local flow of control is confusing and results in poor performance.\n\n**Example:**\n\n\n try {\n if (!Files.isDirectory(PROJECTS)) {\n throw new IllegalStateException(\"Directory not found.\"); // warning: 'throw' caught by containing 'try' statement\n }\n ...\n } catch (Exception e) {\n LOG.error(\"run failed\");\n }\n\nUse the **Ignore rethrown exceptions** option to ignore exceptions that are rethrown." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ThrowCaughtLocally", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MarkedForRemoval", + "shortDescription": { + "text": "Usage of API marked for removal" + }, + "fullDescription": { + "text": "Reports usages of deprecated APIs (classes, fields, and methods) that are marked for removal with '@Deprecated(forRemoval=true)'. The code that uses an API marked for removal may cause a runtime error with a future version of the API. That is why the recommended severity for this inspection is Error. You can change the severity to Warning if you want to use the same code highlighting as in ordinary deprecation. New in 2017.3", + "markdown": "Reports usages of deprecated APIs (classes, fields, and methods) that are marked for removal with `@Deprecated(`**forRemoval**`=true)`.\n\n\nThe code that uses an API marked for removal may cause a runtime error with a future version of the API. That is why\nthe recommended severity for this inspection is *Error*.\n\n\nYou can change the severity to *Warning* if you want to use the same code highlighting as in ordinary deprecation.\n\nNew in 2017.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "removal", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code maturity", + "index": 50, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NestedConditionalExpression", + "shortDescription": { + "text": "Nested conditional expression" + }, + "fullDescription": { + "text": "Reports nested conditional expressions as they may result in extremely confusing code. Example: 'int y = a == 10 ? b == 20 ? 10 : a : b;'", + "markdown": "Reports nested conditional expressions as they may result in extremely confusing code.\n\nExample:\n\n\n int y = a == 10 ? b == 20 ? 10 : a : b;\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NestedConditionalExpression", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SynchronizeOnValueBasedClass", + "shortDescription": { + "text": "Value-based warnings" + }, + "fullDescription": { + "text": "Reports attempts to synchronize on an instance of a value-based class that produce compile-time warnings and raise run-time exceptions starting from Java 16. For example, 'java.lang.Double' is annotated with 'jdk.internal.ValueBased', so the following code will produce a compile-time warning: 'Double d = 20.0;\nsynchronized (d) { ... } // javac warning' New in 2021.1", + "markdown": "Reports attempts to synchronize on an instance of a value-based class that produce compile-time warnings and raise run-time exceptions starting from Java 16.\n\n\nFor example, `java.lang.Double` is annotated with `jdk.internal.ValueBased`, so the following code will\nproduce a compile-time warning:\n\n\n Double d = 20.0;\n synchronized (d) { ... } // javac warning\n\nNew in 2021.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "synchronization", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Compiler issues", + "index": 90, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonSerializableWithSerialVersionUIDField", + "shortDescription": { + "text": "Non-serializable class with 'serialVersionUID'" + }, + "fullDescription": { + "text": "Reports non-'Serializable' classes that define a 'serialVersionUID' field. A 'serialVersionUID' field in that context normally indicates an error because the field will be ignored and the class will not be serialized. Example: 'public class IWantToSerializeThis {\n private static final long serialVersionUID = 2669293150219020249L;\n }'", + "markdown": "Reports non-`Serializable` classes that define a `serialVersionUID` field. A `serialVersionUID` field in that context normally indicates an error because the field will be ignored and the class will not be serialized.\n\n**Example:**\n\n\n public class IWantToSerializeThis {\n private static final long serialVersionUID = 2669293150219020249L;\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "NonSerializableClassWithSerialVersionUID", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ImplicitNumericConversion", + "shortDescription": { + "text": "Implicit numeric conversion" + }, + "fullDescription": { + "text": "Reports implicit conversion between numeric types. Implicit numeric conversion is not a problem in itself but, if unexpected, may cause difficulties when tracing bugs. Example: 'double m(int i) {\n return i * 10;\n }' After the quick-fix is applied: 'double m(int i) {\n return (double) (i * 10);\n }' Configure the inspection: Use the Ignore widening conversions option to ignore implicit conversion that cannot result in data loss (for example, 'int'->'long'). Use the Ignore conversions from and to 'char' option to ignore conversion from and to 'char'. The inspection will still report conversion from and to floating-point numbers. Use the Ignore conversion from constants and literals to make the inspection ignore conversion from literals and compile-time constants.", + "markdown": "Reports implicit conversion between numeric types.\n\nImplicit numeric conversion is not a problem in itself but, if unexpected, may cause difficulties when tracing bugs.\n\n**Example:**\n\n\n double m(int i) {\n return i * 10;\n }\n\nAfter the quick-fix is applied:\n\n\n double m(int i) {\n return (double) (i * 10);\n }\n\nConfigure the inspection:\n\n* Use the **Ignore widening conversions** option to ignore implicit conversion that cannot result in data loss (for example, `int`-\\>`long`).\n* Use the **Ignore conversions from and to 'char'** option to ignore conversion from and to `char`. The inspection will still report conversion from and to floating-point numbers.\n* Use the **Ignore conversion from constants and literals** to make the inspection ignore conversion from literals and compile-time constants." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ImplicitNumericConversion", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BulkFileAttributesRead", + "shortDescription": { + "text": "Bulk 'Files.readAttributes()' call can be used" + }, + "fullDescription": { + "text": "Reports multiple sequential 'java.io.File' attribute checks, such as: 'isDirectory()' 'isFile()' 'lastModified()' 'length()' Such calls can be replaced with a bulk 'Files.readAttributes()' call. This is usually more performant than multiple separate attribute checks. Example: 'boolean isNewFile(File file, long lastModified) throws IOException {\n return file.isFile() && file.lastModified() > lastModified;\n }' After the quick-fix is applied: 'boolean isNewFile(File file, long lastModified) throws IOException {\n var fileAttributes = Files.readAttributes(file.toPath(), BasicFileAttributes.class);\n return fileAttributes.isRegularFile() && fileAttributes.lastModifiedTime().toMillis() > lastModified;\n }' This inspection does not show a warning if 'IOException' is not handled in the current context, but the quick-fix is still available. Note that the replacements are usually not completely equivalent and should be applied with care. In particular, the behavior could differ if the file does not exist at all. This inspection only reports if the language level of the project or module is 7 or higher. New in 2022.1", + "markdown": "Reports multiple sequential `java.io.File` attribute checks, such as:\n\n* `isDirectory()`\n* `isFile()`\n* `lastModified()`\n* `length()`\n\nSuch calls can be replaced with a bulk `Files.readAttributes()` call. This is usually more performant than multiple separate attribute checks.\n\nExample:\n\n\n boolean isNewFile(File file, long lastModified) throws IOException {\n return file.isFile() && file.lastModified() > lastModified;\n }\n\nAfter the quick-fix is applied:\n\n\n boolean isNewFile(File file, long lastModified) throws IOException {\n var fileAttributes = Files.readAttributes(file.toPath(), BasicFileAttributes.class);\n return fileAttributes.isRegularFile() && fileAttributes.lastModifiedTime().toMillis() > lastModified;\n }\n\nThis inspection does not show a warning if `IOException` is not handled in the current context, but the quick-fix is still available.\n\nNote that the replacements are usually not completely equivalent and should be applied with care. In particular, the behavior could differ if\nthe file does not exist at all.\n\nThis inspection only reports if the language level of the project or module is 7 or higher.\n\nNew in 2022.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "BulkFileAttributesRead", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "WhileCanBeForeach", + "shortDescription": { + "text": "'while' loop can be replaced with enhanced 'for' loop" + }, + "fullDescription": { + "text": "Reports 'while' loops that iterate over collections and can be replaced with enhanced 'for' loops (foreach iteration syntax). Example: 'Iterator it = c.iterator();\n while(it.hasNext()) {\n Object obj = it.next();\n System.out.println(obj);\n }' Can be replaced with: 'for (Object obj : c) {\n System.out.println(obj);\n }' This inspection only reports if the language level of the project or module is 5 or higher.", + "markdown": "Reports `while` loops that iterate over collections and can be replaced with enhanced `for` loops (foreach iteration syntax).\n\n**Example:**\n\n\n Iterator it = c.iterator();\n while(it.hasNext()) {\n Object obj = it.next();\n System.out.println(obj);\n }\n\nCan be replaced with:\n\n\n for (Object obj : c) {\n System.out.println(obj);\n }\n\nThis inspection only reports if the language level of the project or module is 5 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "WhileLoopReplaceableByForEach", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 5", + "index": 53, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NotNullFieldNotInitialized", + "shortDescription": { + "text": "@NotNull field is not initialized" + }, + "fullDescription": { + "text": "Reports fields annotated as not-null that are not initialized in the constructor. Example: 'public class MyClass {\n private @NotNull String value;\n\n public void setValue(@NotNull String value) {\n this.value = value;\n }\n\n public @NotNull String getValue() {\n return value;\n }\n}' Such fields may violate the not-null constraint. In the example above, the 'setValue' parameter is annotated as not-null, but 'getValue' may return null if the setter was not called. Configure the inspection: Use the Ignore fields which could be initialized implicitly option to control whether a warning should be issued if the field could be initialized implicitly (e.g. via a dependency injection). Use the Ignore fields initialized in setUp() method option to control whether a warning should be issued if the field is written in the test case 'setUp()' method.", + "markdown": "Reports fields annotated as not-null that are not initialized in the constructor.\n\nExample:\n\n public class MyClass {\n private @NotNull String value;\n\n public void setValue(@NotNull String value) {\n this.value = value;\n }\n\n public @NotNull String getValue() {\n return value;\n }\n }\n\n\nSuch fields may violate the not-null constraint. In the example above, the `setValue` parameter is annotated as not-null, but\n`getValue` may return null if the setter was not called.\n\nConfigure the inspection:\n\n* Use the **Ignore fields which could be initialized implicitly** option to control whether a warning should be issued if the field could be initialized implicitly (e.g. via a dependency injection).\n* Use the **Ignore fields initialized in setUp() method** option to control whether a warning should be issued if the field is written in the test case `setUp()` method." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NotNullFieldNotInitialized", + "cweIds": [ + 476 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs/Nullability problems", + "index": 115, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OverlyComplexBooleanExpression", + "shortDescription": { + "text": "Overly complex boolean expression" + }, + "fullDescription": { + "text": "Reports boolean expressions with too many terms. Such expressions may be confusing and bug-prone. Example: 'cond(x1) && cond(x2) ^ cond(x3) && cond(x4);' Configure the inspection: Use the Maximum number of terms field to specify the maximum number of terms allowed in a boolean expression. Use the Ignore pure conjunctions and disjunctions option to ignore boolean expressions which use only a single boolean operator repeatedly.", + "markdown": "Reports boolean expressions with too many terms. Such expressions may be confusing and bug-prone.\n\nExample:\n\n\n cond(x1) && cond(x2) ^ cond(x3) && cond(x4);\n\nConfigure the inspection:\n\n* Use the **Maximum number of terms** field to specify the maximum number of terms allowed in a boolean expression.\n* Use the **Ignore pure conjunctions and disjunctions** option to ignore boolean expressions which use only a single boolean operator repeatedly." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OverlyComplexBooleanExpression", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NotifyCalledOnCondition", + "shortDescription": { + "text": "'notify()' or 'notifyAll()' called on 'java.util.concurrent.locks.Condition' object" + }, + "fullDescription": { + "text": "Reports calls to 'notify()' or 'notifyAll()' made on 'java.util.concurrent.locks.Condition' object. This is probably a programming error, and some variant of the 'signal()' or 'signalAll()' method was intended instead, otherwise 'IllegalMonitorStateException' may occur. Example: 'class C {\n final Lock l = new ReentrantLock();\n final Condition c = l.newCondition();\n\n void release() {\n l.lock();\n try {\n c.notifyAll(); // probably 'signalAll()' was intended here\n } finally {\n l.unlock();\n }\n }\n }'", + "markdown": "Reports calls to `notify()` or `notifyAll()` made on `java.util.concurrent.locks.Condition` object.\n\n\nThis is probably a programming error, and some variant of the `signal()` or\n`signalAll()` method was intended instead, otherwise `IllegalMonitorStateException` may occur.\n\n**Example:**\n\n\n class C {\n final Lock l = new ReentrantLock();\n final Condition c = l.newCondition();\n\n void release() {\n l.lock();\n try {\n c.notifyAll(); // probably 'signalAll()' was intended here\n } finally {\n l.unlock();\n }\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NotifyCalledOnCondition", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SwitchStatementsWithoutDefault", + "shortDescription": { + "text": "'switch' statement without 'default' branch" + }, + "fullDescription": { + "text": "Reports 'switch' statements that do not contain 'default' labels. Adding the 'default' label guarantees that all possible scenarios are covered, and it becomes easier to make assumptions about the current state of the program. Note that by default, the inspection does not report 'switch' statements if all cases for enums or 'sealed' classes are covered. Use the Ignore exhaustive switch statements option if you want to change this behavior.", + "markdown": "Reports `switch` statements that do not contain `default` labels.\n\nAdding the `default` label guarantees that all possible scenarios are covered, and it becomes\neasier to make assumptions about the current state of the program.\n\n\nNote that by default, the inspection does not report `switch` statements if all cases for enums or `sealed` classes are covered.\nUse the **Ignore exhaustive switch statements** option if you want to change this behavior." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "SwitchStatementWithoutDefaultBranch", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IncompatibleMask", + "shortDescription": { + "text": "Incompatible bitwise mask operation" + }, + "fullDescription": { + "text": "Reports bitwise mask expressions which are guaranteed to evaluate to 'true' or 'false'. The inspection checks the expressions of the form '(var & constant1) == constant2' or '(var | constant1) == constant2', where 'constant1' and 'constant2' are incompatible bitmask constants. Example: '// Incompatible mask: as the mask ends in 00,\n // the result could be 0x1200 but not 0x1234\n if ((mask & 0xFF00) == 0x1234) {...}'", + "markdown": "Reports bitwise mask expressions which are guaranteed to evaluate to `true` or `false`.\n\n\nThe inspection checks the expressions of the form `(var & constant1) == constant2` or\n`(var | constant1) == constant2`, where `constant1`\nand `constant2` are incompatible bitmask constants.\n\n**Example:**\n\n // Incompatible mask: as the mask ends in 00,\n // the result could be 0x1200 but not 0x1234\n if ((mask & 0xFF00) == 0x1234) {...}\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "IncompatibleBitwiseMaskOperation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Bitwise operation issues", + "index": 97, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NewMethodNamingConvention", + "shortDescription": { + "text": "Method naming convention" + }, + "fullDescription": { + "text": "Reports methods whose names are too short, too long, or do not follow the specified regular expression pattern. Instance methods that override library methods and constructors are ignored by this inspection. Example: if the inspection is enabled for static methods, and the minimum specified method name length is 4 (the default), the following static method produces a warning, because the length of its name is 3, which is less than 4: 'public static int max(int a, int b)'. A quick-fix that renames such methods is available only in the editor. Configure the inspection: Use the list in the Options section to specify which methods should be checked. Deselect the checkboxes for the method types for which you want to skip the check. Specify 0 in the length fields to skip the corresponding checks. Regular expressions should be specified in the standard 'java.util.regex' format.", + "markdown": "Reports methods whose names are too short, too long, or do not follow the specified regular expression pattern.\n\nInstance methods that override library\nmethods and constructors are ignored by this inspection.\n\n**Example:** if the inspection is enabled for static methods, and the minimum specified method name length is 4 (the default),\nthe following static method produces a warning, because the length of its name is 3, which is less\nthan 4: `public static int max(int a, int b)`.\n\nA quick-fix that renames such methods is available only in the editor.\n\nConfigure the inspection:\n\nUse the list in the **Options** section to specify which methods should be checked. Deselect the checkboxes for the method types for which\nyou want to skip the check. Specify **0** in the length fields to skip the corresponding checks.\n\nRegular expressions should be specified in the standard `java.util.regex` format." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NewMethodNamingConvention", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions/Method", + "index": 88, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ExplicitArgumentCanBeLambda", + "shortDescription": { + "text": "Explicit argument can be lambda" + }, + "fullDescription": { + "text": "Reports method calls that accept a non-trivial expression and can be replaced with an equivalent method call which accepts a lambda instead. Converting an expression to a lambda ensures that the expression won't be evaluated if it's not used inside the method. For example, 'optional.orElse(createDefaultValue())' can be converted to 'optional.orElseGet(this::createDefaultValue)'. New in 2018.1", + "markdown": "Reports method calls that accept a non-trivial expression and can be replaced with an equivalent method call which accepts a lambda instead.\n\n\nConverting an expression to a lambda ensures that the expression won't be evaluated\nif it's not used inside the method. For example, `optional.orElse(createDefaultValue())` can be converted\nto `optional.orElseGet(this::createDefaultValue)`.\n\nNew in 2018.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ExplicitArgumentCanBeLambda", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EqualsCalledOnEnumConstant", + "shortDescription": { + "text": "'equals()' called on enum value" + }, + "fullDescription": { + "text": "Reports 'equals()' calls on enum constants. Such calls can be replaced by an identity comparison ('==') because two enum constants are equal only when they have the same identity. A quick-fix is available to change the call to a comparison. Example: 'boolean foo(MyEnum value) {\n return value.equals(MyEnum.FOO);\n }' After the quick-fix is applied: 'boolean foo(MyEnum value) {\n return value == MyEnum.FOO;\n }'", + "markdown": "Reports `equals()` calls on enum constants.\n\nSuch calls can be replaced by an identity comparison (`==`) because two\nenum constants are equal only when they have the same identity.\n\nA quick-fix is available to change the call to a comparison.\n\n**Example:**\n\n\n boolean foo(MyEnum value) {\n return value.equals(MyEnum.FOO);\n }\n\nAfter the quick-fix is applied:\n\n\n boolean foo(MyEnum value) {\n return value == MyEnum.FOO;\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EqualsCalledOnEnumConstant", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OverlyStrongTypeCast", + "shortDescription": { + "text": "Overly strong type cast" + }, + "fullDescription": { + "text": "Reports type casts that are overly strong. For instance, casting an object to 'ArrayList' when casting it to 'List' would do just as well. Note: much like the Redundant type cast inspection, applying the fix for this inspection may change the semantics of your program if you are intentionally using an overly strong cast to cause a 'ClassCastException' to be generated. Example: 'interface Super {\n void doSmth();\n }\n interface Sub extends Super { }\n\n void use(Object obj) {\n // Warning: ((Super)obj).doSmth() could be used\n ((Sub)obj).doSmth();\n }' Use the checkbox below to ignore casts when there's a matching 'instanceof' check in the code.", + "markdown": "Reports type casts that are overly strong. For instance, casting an object to `ArrayList` when casting it to `List` would do just as well.\n\n\n**Note:** much like the *Redundant type cast*\ninspection, applying the fix for this inspection may change the semantics of your program if you are\nintentionally using an overly strong cast to cause a `ClassCastException` to be generated.\n\nExample:\n\n\n interface Super {\n void doSmth();\n }\n interface Sub extends Super { }\n\n void use(Object obj) {\n // Warning: ((Super)obj).doSmth() could be used\n ((Sub)obj).doSmth();\n }\n\n\nUse the checkbox below to ignore casts when there's a matching `instanceof` check in the code." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OverlyStrongTypeCast", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Abstraction issues", + "index": 78, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JavaLangImport", + "shortDescription": { + "text": "Unnecessary import from the 'java.lang' package" + }, + "fullDescription": { + "text": "Reports 'import' statements that refer to the 'java.lang' package. 'java.lang' classes are always implicitly imported, so such import statements are redundant and confusing. Since IntelliJ IDEA can automatically detect and fix such statements with its Optimize Imports command, this inspection is mostly useful for offline reporting on code bases that you don't intend to change.", + "markdown": "Reports `import` statements that refer to the `java.lang` package.\n\n\n`java.lang` classes are always implicitly imported, so such import statements are\nredundant and confusing.\n\n\nSince IntelliJ IDEA can automatically detect and fix such statements with its **Optimize Imports** command, this inspection is mostly useful for offline reporting on code bases that you don't intend to change." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "JavaLangImport", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Imports", + "index": 22, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UtilityClass", + "shortDescription": { + "text": "Utility class" + }, + "fullDescription": { + "text": "Reports utility classes. Utility classes have all fields and methods declared as 'static' and their presence may indicate a lack of object-oriented design. Use the Ignore if annotated by option to specify special annotations. The inspection ignores classes annotated with one of these annotations.", + "markdown": "Reports utility classes.\n\nUtility classes have all fields and methods declared as `static` and their\npresence may indicate a lack of object-oriented design.\n\n\nUse the **Ignore if annotated by** option to specify special annotations. The inspection ignores classes annotated with one of\nthese annotations." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UtilityClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassNameDiffersFromFileName", + "shortDescription": { + "text": "Class name differs from file name" + }, + "fullDescription": { + "text": "Reports top-level class names that don't match the name of a file containing them. While the Java specification allows for naming non-'public' classes this way, files with unmatched names may be confusing and decrease usefulness of various software tools.", + "markdown": "Reports top-level class names that don't match the name of a file containing them.\n\nWhile the Java specification allows for naming non-`public` classes this way,\nfiles with unmatched names may be confusing and decrease usefulness of various software tools." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassNameDiffersFromFileName", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "HardcodedLineSeparators", + "shortDescription": { + "text": "Hardcoded line separator" + }, + "fullDescription": { + "text": "Reports linefeed ('\\n') and carriage return ('\\r') character escape sequences used in string literals, character literals or text blocks. These characters are commonly used as line separators, and portability may suffer if they are hardcoded. Example: 'String count = \"first\\nsecond\\rthird\";'", + "markdown": "Reports linefeed (`\\n`) and carriage return (`\\r`) character escape sequences used in string literals, character literals or text blocks. These characters are commonly used as line separators, and portability may suffer if they are hardcoded.\n\n**Example:**\n\n\n String count = \"first\\nsecond\\rthird\";\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "HardcodedLineSeparator", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Portability", + "index": 7, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryConstantArrayCreationExpression", + "shortDescription": { + "text": "Redundant 'new' expression in constant array creation" + }, + "fullDescription": { + "text": "Reports constant new array expressions that can be replaced with an array initializer. Array initializers can omit the type because it is already specified in the left side of the assignment. Example: 'int[] foo = new int[] {42};' After the quick-fix is applied: 'int[] foo = {42};'", + "markdown": "Reports constant new array expressions that can be replaced with an array initializer. Array initializers can omit the type because it is already specified in the left side of the assignment.\n\n**Example:**\n\n\n int[] foo = new int[] {42};\n\nAfter the quick-fix is applied:\n\n\n int[] foo = {42};\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryConstantArrayCreationExpression", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LabeledStatement", + "shortDescription": { + "text": "Labeled statement" + }, + "fullDescription": { + "text": "Reports labeled statements that can complicate refactorings and control flow of the method. Example: 'label:\n while (true) {\n // code\n }'", + "markdown": "Reports labeled statements that can complicate refactorings and control flow of the method.\n\nExample:\n\n\n label:\n while (true) {\n // code\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LabeledStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IndexOfReplaceableByContains", + "shortDescription": { + "text": "'String.indexOf()' expression can be replaced with 'contains()'" + }, + "fullDescription": { + "text": "Reports comparisons with 'String.indexOf()' calls that can be replaced with a call to the 'String.contains()' method. Example: 'boolean b = \"abcd\".indexOf('e') >= 0;' After the quick-fix is applied: 'boolean b = \"abcd\".contains('e');' This inspection only reports if the language level of the project or module is 5 or higher.", + "markdown": "Reports comparisons with `String.indexOf()` calls that can be replaced with a call to the `String.contains()` method.\n\n**Example:**\n\n\n boolean b = \"abcd\".indexOf('e') >= 0;\n\nAfter the quick-fix is applied:\n\n\n boolean b = \"abcd\".contains('e');\n\nThis inspection only reports if the language level of the project or module is 5 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "IndexOfReplaceableByContains", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 5", + "index": 53, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringConcatenationArgumentToLogCall", + "shortDescription": { + "text": "Non-constant string concatenation as argument to logging call" + }, + "fullDescription": { + "text": "Reports non-constant string concatenations that are used as arguments to SLF4J and Log4j 2 logging methods. Non-constant concatenations are evaluated at runtime even when the logging message is not logged; this can negatively impact performance. It is recommended to use a parameterized log message instead, which will not be evaluated when logging is disabled. Example: 'public class Vital {\n private static final Logger LOG = LoggerFactory.getLogger(Vital.class);\n\n public void saveTheWorld(int i, String s, boolean b) {\n LOG.info(\"saveTheWorld(\" + i + \", \" + s + \", \" + b + \")\");\n // todo\n }\n }' After the quick-fix is applied: 'public class Vital {\n private static final Logger LOG = LoggerFactory.getLogger(Vital.class);\n\n public void saveTheWorld(int i, String s, boolean b) {\n LOG.info(\"saveTheWorld({}, {}, {})\", i, s, b);\n // todo\n }\n }' Configure the inspection: Use the Warn on list to ignore certain higher logging levels. Higher logging levels may be enabled even in production, and the arguments will always be evaluated.", + "markdown": "Reports non-constant string concatenations that are used as arguments to **SLF4J** and **Log4j 2** logging methods. Non-constant concatenations are evaluated at runtime even when the logging message is not logged; this can negatively impact performance. It is recommended to use a parameterized log message instead, which will not be evaluated when logging is disabled.\n\n**Example:**\n\n\n public class Vital {\n private static final Logger LOG = LoggerFactory.getLogger(Vital.class);\n\n public void saveTheWorld(int i, String s, boolean b) {\n LOG.info(\"saveTheWorld(\" + i + \", \" + s + \", \" + b + \")\");\n // todo\n }\n }\n\nAfter the quick-fix is applied:\n\n\n public class Vital {\n private static final Logger LOG = LoggerFactory.getLogger(Vital.class);\n\n public void saveTheWorld(int i, String s, boolean b) {\n LOG.info(\"saveTheWorld({}, {}, {})\", i, s, b);\n // todo\n }\n }\n\n\nConfigure the inspection:\n\n* Use the **Warn on** list to ignore certain higher logging levels. Higher logging levels may be enabled even in production, and the arguments will always be evaluated." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StringConcatenationArgumentToLogCall", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Logging", + "index": 68, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspiciousNameCombination", + "shortDescription": { + "text": "Suspicious variable/parameter name combination" + }, + "fullDescription": { + "text": "Reports assignments and function calls in which the name of the target variable or the function parameter does not match the name of the value assigned to it. Example 1: 'int x = 0;\n int y = x; // x is used as a y-coordinate' Example 2: 'int x = 0, y = 0;\n // x is used as a y-coordinate and y as an x-coordinate\n Rectangle rc = new Rectangle(y, x, 20, 20);' Configure the inspection: Use the Group of names area to specify the names which should not be used together: an error is reported if the parameter name or assignment target name contains words from one group and the name of the assigned or passed variable contains words from a different group. Use the Ignore methods area to specify the methods that should not be checked but have a potentially suspicious name. For example, the 'Integer.compare()' parameters are named 'x' and 'y' but are unrelated to coordinates.", + "markdown": "Reports assignments and function calls in which the name of the target variable or the function parameter does not match the name of the value assigned to it.\n\nExample 1:\n\n\n int x = 0;\n int y = x; // x is used as a y-coordinate\n \nExample 2:\n\n\n int x = 0, y = 0;\n // x is used as a y-coordinate and y as an x-coordinate\n Rectangle rc = new Rectangle(y, x, 20, 20);\n\nConfigure the inspection:\n\nUse the **Group of names** area to specify the names which should not be used together: an error is reported\nif the parameter name or assignment target name contains words from one group and the name of the assigned or passed\nvariable contains words from a different group.\n\nUse the **Ignore methods** area to specify the methods that should not be checked but have a potentially suspicious name.\nFor example, the `Integer.compare()` parameters are named `x` and `y` but are unrelated to coordinates." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SuspiciousNameCombination", + "cweIds": [ + 628 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ImplicitDefaultCharsetUsage", + "shortDescription": { + "text": "Implicit platform default charset" + }, + "fullDescription": { + "text": "Reports method and constructor calls that implicitly use the platform default charset. Such calls can produce different results on systems that use a different default charset and may result in unexpected behaviour. Example: 'void foo(byte[] bytes) {\n String s = new String(bytes);\n}'\n You can use a quick-fix that specifies the explicit UTF-8 charset if the corresponding overloaded method is available. After the quick-fix is applied: 'void foo(byte[] bytes) {\n String s = new String(bytes, StandardCharsets.UTF_8);\n}'", + "markdown": "Reports method and constructor calls that implicitly use the platform default charset. Such calls can produce different results on systems that use a different default charset and may result in unexpected behaviour.\n\n**Example:**\n\n void foo(byte[] bytes) {\n String s = new String(bytes);\n }\n\nYou can use a quick-fix that specifies the explicit UTF-8 charset if the corresponding overloaded method is available.\nAfter the quick-fix is applied:\n\n void foo(byte[] bytes) {\n String s = new String(bytes, StandardCharsets.UTF_8);\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ImplicitDefaultCharsetUsage", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Internationalization", + "index": 9, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DesignForExtension", + "shortDescription": { + "text": "Design for extension" + }, + "fullDescription": { + "text": "Reports methods which are not 'static', 'private', 'final' or 'abstract', and whose bodies are not empty. Coding in a style that avoids such methods protects the contracts of classes from being broken by their subclasses. The benefit of this style is that subclasses cannot corrupt the state of the superclass by forgetting to call the super method. The cost is that subclasses are limited in their flexibility, in particular they cannot prevent execution of code in the superclass. Use the quick-fix to add the missing modifiers. Example: 'class Foo {\n public boolean equals(Object o) { return true; }\n }' After the quick-fix is applied: 'class Foo {\n public final boolean equals(Object o) { return true; }\n }' This inspection is intended for code that is going to be used in secure environments, and is probably not appropriate for less restrictive environments.", + "markdown": "Reports methods which are not `static`, `private`, `final` or `abstract`, and whose bodies are not empty.\n\n\nCoding in a style that avoids such methods protects the contracts of classes from being broken by their subclasses. The\nbenefit of this style is that subclasses cannot corrupt the state of the superclass by forgetting to call the super method. The cost is\nthat\nsubclasses are limited in their flexibility, in particular they cannot prevent execution of code in the superclass. Use the quick-fix to\nadd\nthe missing modifiers.\n\n**Example:**\n\n\n class Foo {\n public boolean equals(Object o) { return true; }\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo {\n public final boolean equals(Object o) { return true; }\n }\n\nThis inspection is intended for code that is going to be used in secure environments, and is probably not appropriate for less restrictive environments." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "DesignForExtension", + "cweIds": [ + 668 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Security", + "index": 30, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SimplifyStreamApiCallChains", + "shortDescription": { + "text": "Stream API call chain can be simplified" + }, + "fullDescription": { + "text": "Reports stream API call chains that can be simplified. Simplification will often avoid some temporary object creation during collection traversal. The inspection replaces the following call chains: 'collection.stream().forEach()' → 'collection.forEach()' 'collection.stream().collect(toList/toSet/toCollection())' → 'new CollectionType<>(collection)' 'collection.stream().toArray()' → 'collection.toArray()' 'Arrays.asList().stream()' → 'Arrays.stream()' or 'Stream.of()' 'IntStream.range(0, array.length).mapToObj(idx -> array[idx])' → 'Arrays.stream(array)' 'IntStream.range(0, list.size()).mapToObj(idx -> list.get(idx))' → 'list.stream()' 'Collections.singleton().stream()' → 'Stream.of()' 'Collections.emptyList().stream()' → 'Stream.empty()' 'stream.filter().findFirst().isPresent()' → 'stream.anyMatch()' 'stream.collect(counting())' → 'stream.count()' 'stream.collect(maxBy())' → 'stream.max()' 'stream.collect(mapping())' → 'stream.map().collect()' 'stream.collect(reducing())' → 'stream.reduce()' 'stream.collect(summingInt())' → 'stream.mapToInt().sum()' 'stream.mapToObj(x -> x)' → 'stream.boxed()' 'stream.map(x -> {...; return x;})' → 'stream.peek(x -> ...)' '!stream.anyMatch()' → 'stream.noneMatch()' '!stream.anyMatch(x -> !(...))' → 'stream.allMatch()' 'stream.map().anyMatch(Boolean::booleanValue)' → 'stream.anyMatch()' 'IntStream.range(expr1, expr2).mapToObj(x -> array[x])' → 'Arrays.stream(array, expr1, expr2)' 'Collection.nCopies(count, ...)' → 'Stream.generate().limit(count)' 'stream.sorted(comparator).findFirst()' → 'Stream.min(comparator)' 'optional.orElseGet(() -> { throw new ...; })' → 'optional.orElseThrow()' Note that the replacement semantics may have minor differences in some cases. For example, 'Collections.synchronizedList(...).stream().forEach()' is not synchronized while 'Collections.synchronizedList(...).forEach()' is synchronized. Also, 'collect(Collectors.maxBy())' returns an empty 'Optional' if the resulting element is 'null' while 'Stream.max()' throws 'NullPointerException' in this case.", + "markdown": "Reports stream API call chains that can be simplified. Simplification will often avoid some temporary object creation during collection traversal.\n\n\nThe inspection replaces the following call chains:\n\n* `collection.stream().forEach()` → `collection.forEach()`\n* `collection.stream().collect(toList/toSet/toCollection())` → `new CollectionType<>(collection)`\n* `collection.stream().toArray()` → `collection.toArray()`\n* `Arrays.asList().stream()` → `Arrays.stream()` or `Stream.of()`\n* `IntStream.range(0, array.length).mapToObj(idx -> array[idx])` → `Arrays.stream(array)`\n* `IntStream.range(0, list.size()).mapToObj(idx -> list.get(idx))` → `list.stream()`\n* `Collections.singleton().stream()` → `Stream.of()`\n* `Collections.emptyList().stream()` → `Stream.empty()`\n* `stream.filter().findFirst().isPresent()` → `stream.anyMatch()`\n* `stream.collect(counting())` → `stream.count()`\n* `stream.collect(maxBy())` → `stream.max()`\n* `stream.collect(mapping())` → `stream.map().collect()`\n* `stream.collect(reducing())` → `stream.reduce()`\n* `stream.collect(summingInt())` → `stream.mapToInt().sum()`\n* `stream.mapToObj(x -> x)` → `stream.boxed()`\n* `stream.map(x -> {...; return x;})` → `stream.peek(x -> ...)`\n* `!stream.anyMatch()` → `stream.noneMatch()`\n* `!stream.anyMatch(x -> !(...))` → `stream.allMatch()`\n* `stream.map().anyMatch(Boolean::booleanValue)` → `stream.anyMatch()`\n* `IntStream.range(expr1, expr2).mapToObj(x -> array[x])` → `Arrays.stream(array, expr1, expr2)`\n* `Collection.nCopies(count, ...)` → `Stream.generate().limit(count)`\n* `stream.sorted(comparator).findFirst()` → `Stream.min(comparator)`\n* `optional.orElseGet(() -> { throw new ...; })` → `optional.orElseThrow()`\n\n\nNote that the replacement semantics may have minor differences in some cases. For example,\n`Collections.synchronizedList(...).stream().forEach()` is not synchronized while\n`Collections.synchronizedList(...).forEach()` is synchronized.\nAlso, `collect(Collectors.maxBy())` returns an empty `Optional` if the resulting element is\n`null` while `Stream.max()` throws `NullPointerException` in this case." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SimplifyStreamApiCallChains", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OptionalContainsCollection", + "shortDescription": { + "text": "'Optional' contains array or collection" + }, + "fullDescription": { + "text": "Reports 'java.util.Optional' or 'com.google.common.base.Optional' types with an array or collection type parameter. In such cases, it is more clear to just use an empty array or collection to indicate the absence of result. Example: 'Optional> foo() {\n return Optional.empty();\n }' This code could look like: 'List foo() {\n return new List<>();\n }'", + "markdown": "Reports `java.util.Optional` or `com.google.common.base.Optional` types with an array or collection type parameter.\n\nIn such cases, it is more clear to just use an empty array or collection to indicate the absence of result.\n\n**Example:**\n\n\n Optional> foo() {\n return Optional.empty();\n }\n\nThis code could look like:\n\n\n List foo() {\n return new List<>();\n }\n \n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OptionalContainsCollection", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnknownGuard", + "shortDescription": { + "text": "Unknown '@GuardedBy' field" + }, + "fullDescription": { + "text": "Reports '@GuardedBy' annotations in which the specified guarding field is unknown. Example: 'private Object state;\n\n @GuardedBy(\"lock\") //unknown guard reference\n public void bar() {\n state = new Object();\n }' Supported '@GuardedBy' annotations are: 'net.jcip.annotations.GuardedBy' 'javax.annotation.concurrent.GuardedBy' 'org.apache.http.annotation.GuardedBy' 'com.android.annotations.concurrency.GuardedBy' 'androidx.annotation.GuardedBy' 'com.google.errorprone.annotations.concurrent.GuardedBy'", + "markdown": "Reports `@GuardedBy` annotations in which the specified guarding field is unknown.\n\nExample:\n\n\n private Object state;\n\n @GuardedBy(\"lock\") //unknown guard reference\n public void bar() {\n state = new Object();\n }\n\nSupported `@GuardedBy` annotations are:\n\n* `net.jcip.annotations.GuardedBy`\n* `javax.annotation.concurrent.GuardedBy`\n* `org.apache.http.annotation.GuardedBy`\n* `com.android.annotations.concurrency.GuardedBy`\n* `androidx.annotation.GuardedBy`\n* `com.google.errorprone.annotations.concurrent.GuardedBy`" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnknownGuard", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Concurrency annotation issues", + "index": 61, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InterfaceMethodClashesWithObject", + "shortDescription": { + "text": "Interface method clashes with method in 'Object'" + }, + "fullDescription": { + "text": "Reports interface methods that clash with the protected methods 'clone()' and 'finalize()' from the 'java.lang.Object' class. In an interface, it is possible to declare these methods with a return type that is incompatible with the 'java.lang.Object' methods. A class that implements such an interface will not be compilable. When the interface is functional, it remains possible to create a lambda from it, but this is not recommended. Example: '// Warning: this interface cannot be implemented\n // by any class, only by a lambda or method reference\n interface MyInterface {\n double clone();\n }'", + "markdown": "Reports interface methods that clash with the **protected** methods `clone()` and `finalize()` from the `java.lang.Object` class.\n\nIn an interface, it is possible to declare these methods with a return type that is incompatible with the `java.lang.Object` methods.\nA class that implements such an interface will not be compilable.\nWhen the interface is functional, it remains possible to create a lambda from it, but this is not recommended.\n\nExample:\n\n\n // Warning: this interface cannot be implemented\n // by any class, only by a lambda or method reference\n interface MyInterface {\n double clone();\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "InterfaceMethodClashesWithObject", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Abstraction issues", + "index": 78, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LoadLibraryWithNonConstantString", + "shortDescription": { + "text": "Call to 'System.loadLibrary()' with non-constant string" + }, + "fullDescription": { + "text": "Reports calls to 'java.lang.System.loadLibrary()', 'java.lang.System.load()', 'java.lang.Runtime.loadLibrary()' and 'java.lang.Runtime.load()' which take a dynamically-constructed string as the name of the library. Constructed library name strings are a common source of security breaches. By default, this inspection ignores compile-time constants. Example: 'void test(int i) {\n System.loadLibrary(\"foo\" + i);\n }' Use the inspection settings to consider any 'static final' fields as constant. Be careful, because strings like the following will be ignored when the option is enabled: 'private static final String LIBRARY = getUserInput();'", + "markdown": "Reports calls to `java.lang.System.loadLibrary()`, `java.lang.System.load()`, `java.lang.Runtime.loadLibrary()` and `java.lang.Runtime.load()` which take a dynamically-constructed string as the name of the library.\n\n\nConstructed library name strings are a common source of security breaches.\nBy default, this inspection ignores compile-time constants.\n\n**Example:**\n\n\n void test(int i) {\n System.loadLibrary(\"foo\" + i);\n }\n\n\nUse the inspection settings to consider any `static final` fields as constant.\nBe careful, because strings like the following will be ignored when the option is enabled:\n\n\n private static final String LIBRARY = getUserInput();\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LoadLibraryWithNonConstantString", + "cweIds": [ + 114, + 494, + 676, + 829 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Security", + "index": 30, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SimplifiableAssertion", + "shortDescription": { + "text": "Simplifiable assertion" + }, + "fullDescription": { + "text": "Reports any 'assert' calls that can be replaced with simpler and equivalent calls. Example → Replacement 'assertEquals(true, x());' 'assertTrue(x());' 'assertTrue(y() != null);' 'assertNotNull(y());' 'assertTrue(z == z());' 'assertSame(z, z());' 'assertTrue(a.equals(a()));' 'assertEquals(a, a());' 'assertTrue(false);' 'fail();'", + "markdown": "Reports any `assert` calls that can be replaced with simpler and equivalent calls.\n\n| Example | → | Replacement |\n|----------------------------------|---|-------------------------|\n| `assertEquals(`**true**`, x());` | | `assertTrue(x());` |\n| `assertTrue(y() != null);` | | `assertNotNull(y());` |\n| `assertTrue(z == z());` | | `assertSame(z, z());` |\n| `assertTrue(a.equals(a()));` | | `assertEquals(a, a());` |\n| `assertTrue(`**false**`);` | | `fail();` |" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SimplifiableAssertion", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Test frameworks", + "index": 96, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StaticMethodOnlyUsedInOneClass", + "shortDescription": { + "text": "Static member only used from one other class" + }, + "fullDescription": { + "text": "Reports 'static' methods and fields that are only used from a class other than the containing class. Such members could be moved into the using class. Factory methods and members accessed from an anonymous class inside the member's class are ignored by this inspection. Convenience overloads, which call a method with the same name in the same class but have fewer parameters, are also ignored. Use the first checkbox to suppress this inspection when the static member is only used from a test class. Use the second checkbox below to ignore member usages from inside anonymous, local, or non-static inner classes. Use the third checkbox below to not warn on members that cannot be moved without problems, for example, because a method with an identical signature is already present in the target class, or because a field or a method used inside the method will not be accessible when this method is moved. Use the fourth checkbox to ignore members located in utility classes.", + "markdown": "Reports `static` methods and fields that are only used from a class other than the containing class. Such members could be moved into the using class. Factory methods and members accessed from an anonymous class inside the member's class are ignored by this inspection. Convenience overloads, which call a method with the same name in the same class but have fewer parameters, are also ignored.\n\n\nUse the first checkbox to suppress this inspection when the static member is only used from a test class.\n\n\nUse the second checkbox below to ignore member usages from inside anonymous, local, or non-static inner classes.\n\n\nUse the third checkbox below to not warn on members that cannot be moved without problems,\nfor example, because a method with an identical signature is already present in the target class,\nor because a field or a method used inside the method will not be accessible when this method is moved.\n\n\nUse the fourth checkbox to ignore members located in utility classes." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StaticMethodOnlyUsedInOneClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Abstraction issues", + "index": 78, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AbstractClassExtendsConcreteClass", + "shortDescription": { + "text": "Abstract class extends concrete class" + }, + "fullDescription": { + "text": "Reports 'abstract' classes that extend concrete classes.", + "markdown": "Reports `abstract` classes that extend concrete classes." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AbstractClassExtendsConcreteClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Inheritance issues", + "index": 25, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantEscapeInRegexReplacement", + "shortDescription": { + "text": "Redundant escape in regex replacement string" + }, + "fullDescription": { + "text": "Reports redundant escapes in the replacement string of regex methods. It is allowed to escape any character in a regex replacement string, but only for the '$' and '\\' characters is escaping necessary. Example: 'string.replaceAll(\"a\", \"\\\\b\");' After the quick-fix is applied: 'string.replaceAll(\"a\", \"b\");' New in 2022.3", + "markdown": "Reports redundant escapes in the replacement string of regex methods. It is allowed to escape any character in a regex replacement string, but only for the `$` and `\\` characters is escaping necessary.\n\n**Example:**\n\n\n string.replaceAll(\"a\", \"\\\\b\");\n\nAfter the quick-fix is applied:\n\n\n string.replaceAll(\"a\", \"b\");\n\nNew in 2022.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantEscapeInRegexReplacement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java8CollectionRemoveIf", + "shortDescription": { + "text": "Loop can be replaced with 'Collection.removeIf()'" + }, + "fullDescription": { + "text": "Reports loops which can be collapsed into a single 'Collection.removeIf' call. Example: 'for (Iterator it = collection.iterator(); it.hasNext(); ) {\n String aValue = it.next();\n if(shouldBeRemoved(aValue)) {\n it.remove();\n }\n }' After the quick-fix is applied: 'collection.removeIf(aValue -> shouldBeRemoved(aValue));' This inspection only reports if the language level of the project or module is 8 or higher.", + "markdown": "Reports loops which can be collapsed into a single `Collection.removeIf` call.\n\nExample:\n\n\n for (Iterator it = collection.iterator(); it.hasNext(); ) {\n String aValue = it.next();\n if(shouldBeRemoved(aValue)) {\n it.remove();\n }\n }\n\nAfter the quick-fix is applied:\n\n\n collection.removeIf(aValue -> shouldBeRemoved(aValue));\n\n\nThis inspection only reports if the language level of the project or module is 8 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "Java8CollectionRemoveIf", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 8", + "index": 62, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SynchronizedOnLiteralObject", + "shortDescription": { + "text": "Synchronization on an object initialized with a literal" + }, + "fullDescription": { + "text": "Reports 'synchronized' blocks that lock on an object initialized with a literal. String literals are interned and 'Character', 'Boolean' and 'Number' literals can be allocated from a cache. Because of this, it is possible that some other part of the system, which uses an object initialized with the same literal, is actually holding a reference to the exact same object. This can create unexpected dead-lock situations, if the lock object was thought to be private. Example: 'class Main {\n final String mutex = \"Mutex\";\n void method() {\n synchronized (mutex) {\n }\n }\n }' Use the Warn on all possible literals option to report any synchronization on 'String', 'Character', 'Boolean' and 'Number' objects.", + "markdown": "Reports `synchronized` blocks that lock on an object initialized with a literal.\n\n\nString literals are interned and `Character`, `Boolean` and `Number` literals can be allocated from a cache.\nBecause of this, it is possible that some other part of the system, which uses an object initialized with the same literal, is actually\nholding a reference to the exact same object. This can create unexpected dead-lock situations, if the lock object was thought to be private.\n\n**Example:**\n\n\n class Main {\n final String mutex = \"Mutex\";\n void method() {\n synchronized (mutex) {\n }\n }\n }\n\n\nUse the **Warn on all possible literals** option to report any synchronization on\n`String`, `Character`, `Boolean` and `Number` objects." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SynchronizedOnLiteralObject", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java9ReflectionClassVisibility", + "shortDescription": { + "text": "Reflective access across modules issues" + }, + "fullDescription": { + "text": "Reports 'Class.forName()' and 'ClassLoader.loadClass()' calls which try to access classes that aren't visible in the current scope due to Java 9 module accessibility rules. This inspection only reports if the language level of the project or module is 9 or higher.", + "markdown": "Reports `Class.forName()` and `ClassLoader.loadClass()` calls which try to access classes that aren't visible in the current scope due to Java 9 module accessibility rules.\n\nThis inspection only reports if the language level of the project or module is 9 or higher." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "Java9ReflectionClassVisibility", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Reflective access", + "index": 98, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CharsetObjectCanBeUsed", + "shortDescription": { + "text": "Standard 'Charset' object can be used" + }, + "fullDescription": { + "text": "Reports methods and constructors in which constant charset 'String' literal (for example, '\"UTF-8\"') can be replaced with the predefined 'StandardCharsets.UTF_8' code. The code after the fix may work faster, because the charset lookup becomes unnecessary. Also, catching 'UnsupportedEncodingException' may become unnecessary as well. In this case, the catch block will be removed automatically. Example: 'try {\n byte[] bytes = \"str\".getBytes(\"UTF-8\");\n } catch (UnsupportedEncodingException e) {\n }' After quick-fix is applied: 'byte[] bytes = \"str\".getBytes(StandardCharsets.UTF_8);' The inspection is available in Java 7 and later. New in 2018.2", + "markdown": "Reports methods and constructors in which constant charset `String` literal (for example, `\"UTF-8\"`) can be replaced with the predefined `StandardCharsets.UTF_8` code.\n\nThe code after the fix may work faster, because the charset lookup becomes unnecessary.\nAlso, catching `UnsupportedEncodingException` may become unnecessary as well. In this case,\nthe catch block will be removed automatically.\n\nExample:\n\n\n try {\n byte[] bytes = \"str\".getBytes(\"UTF-8\");\n } catch (UnsupportedEncodingException e) {\n }\n\nAfter quick-fix is applied:\n\n\n byte[] bytes = \"str\".getBytes(StandardCharsets.UTF_8);\n\nThe inspection is available in Java 7 and later.\n\nNew in 2018.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CharsetObjectCanBeUsed", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SafeVarargsDetector", + "shortDescription": { + "text": "Possible heap pollution from parameterized vararg type" + }, + "fullDescription": { + "text": "Reports methods with variable arity, which can be annotated as '@SafeVarargs'. The '@SafeVarargs' annotation suppresses unchecked warnings about parameterized array creation at call sites. Example: 'public class Foo {\n private List list = new ArrayList<>();\n\n public final void safeVarargs(T... elements) {\n Collections.addAll(list, elements);\n }\n }' After the quick-fix is applied: 'public class Foo {\n private List list = new ArrayList<>();\n\n @SafeVarargs\n public final void safeVarargs(T... elements) {\n Collections.addAll(list, elements);\n }\n }' This annotation is not supported under Java 1.6 or earlier JVMs.", + "markdown": "Reports methods with variable arity, which can be annotated as `@SafeVarargs`. The `@SafeVarargs` annotation suppresses unchecked warnings about parameterized array creation at call sites.\n\n**Example:**\n\n\n public class Foo {\n private List list = new ArrayList<>();\n\n public final void safeVarargs(T... elements) {\n Collections.addAll(list, elements);\n }\n }\n\nAfter the quick-fix is applied:\n\n\n public class Foo {\n private List list = new ArrayList<>();\n\n @SafeVarargs\n public final void safeVarargs(T... elements) {\n Collections.addAll(list, elements);\n }\n }\n\n\nThis annotation is not supported under Java 1.6 or earlier JVMs." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "unchecked", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 7", + "index": 112, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ComparatorMethodParameterNotUsed", + "shortDescription": { + "text": "Suspicious 'Comparator.compare()' implementation" + }, + "fullDescription": { + "text": "Reports problems in 'Comparator.compare()' and 'Comparable.compareTo()' implementations. The following cases are reported: A parameter is not used. Most likely this is a typo and the other parameter is compared with itself, or the method is not implemented correctly. It's evident that the method does not return '0' for the same elements. Such a comparison method violates the contract and can produce unpredictable results when equal elements are encountered. In particular, sorting may fail with an exception on some data. The comparison method never returns positive or negative value. To fulfill the contract, if the comparison method returns positive values, it should also return negative ones if arguments are supplied in reversed order. The comparison method returns 'Integer.MIN_VALUE'. While allowed by the contract, it may be error-prone, as some call sites may incorrectly try to invert the return value of the comparison method using the unary minus operator. The negated value of 'Integer.MIN_VALUE' is 'Integer.MIN_VALUE'. Example: 'Comparator lambda =\n (a, b) -> a.length() > b.length()\n ? 0\n : Math.random() > 0.5 ? -1 : 1;'", + "markdown": "Reports problems in `Comparator.compare()` and `Comparable.compareTo()` implementations.\n\nThe following cases are reported:\n\n* A parameter is not used. Most likely this is a typo and the other parameter is compared with itself, or the method is not implemented correctly.\n* It's evident that the method does not return `0` for the same elements. Such a comparison method violates the contract and can produce unpredictable results when equal elements are encountered. In particular, sorting may fail with an exception on some data.\n* The comparison method never returns positive or negative value. To fulfill the contract, if the comparison method returns positive values, it should also return negative ones if arguments are supplied in reversed order.\n* The comparison method returns `Integer.MIN_VALUE`. While allowed by the contract, it may be error-prone, as some call sites may incorrectly try to invert the return value of the comparison method using the unary minus operator. The negated value of `Integer.MIN_VALUE` is `Integer.MIN_VALUE`.\n\n**Example:**\n\n\n Comparator lambda =\n (a, b) -> a.length() > b.length()\n ? 0\n : Math.random() > 0.5 ? -1 : 1;\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ComparatorMethodParameterNotUsed", + "cweIds": [ + 628 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UseOfSunClasses", + "shortDescription": { + "text": "Use of 'sun.*' classes" + }, + "fullDescription": { + "text": "Reports uses of classes from the 'sun.*' hierarchy. Such classes are non-portable between different JVMs.", + "markdown": "Reports uses of classes from the `sun.*` hierarchy. Such classes are non-portable between different JVMs." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UseOfSunClasses", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Portability", + "index": 7, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantCast", + "shortDescription": { + "text": "Redundant type cast" + }, + "fullDescription": { + "text": "Reports unnecessary cast expressions. Example: 'static Object toObject(String s) {\n return (Object) s;\n }' Use the checkbox below to ignore clarifying casts e.g., casts in collection calls where 'Object' is expected: 'static void removeFromList(List l, Object o) {\n l.remove((String)o);\n }'", + "markdown": "Reports unnecessary cast expressions.\n\nExample:\n\n\n static Object toObject(String s) {\n return (Object) s;\n }\n\n\nUse the checkbox below to ignore clarifying casts e.g., casts in collection calls where `Object` is expected:\n\n\n static void removeFromList(List l, Object o) {\n l.remove((String)o);\n } \n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantCast", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AnonymousInnerClass", + "shortDescription": { + "text": "Anonymous inner class can be replaced with inner class" + }, + "fullDescription": { + "text": "Reports anonymous inner classes. In some cases, replacing anonymous inner classes with inner classes can lead to more readable and maintainable code. Also, some code standards discourage anonymous inner classes.", + "markdown": "Reports anonymous inner classes.\n\nIn some cases, replacing anonymous inner classes with inner classes can lead to more readable and maintainable code.\nAlso, some code standards discourage anonymous inner classes." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AnonymousInnerClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SimplifyCollector", + "shortDescription": { + "text": "Simplifiable collector" + }, + "fullDescription": { + "text": "Reports collectors that can be simplified. In particular, some cascaded 'groupingBy()' collectors can be expressed by using a simpler 'toMap()' collector, which is also likely to be more performant. Example: 'Collectors.groupingByConcurrent(String::length, Collectors.collectingAndThen(Collectors.maxBy(String::compareTo), Optional::get));' After the quick-fix is applied: 'Collectors.toConcurrentMap(String::length, Function.identity(), BinaryOperator.maxBy(String::compareTo));' This inspection only reports if the language level of the project or module is 8 or higher. New in 2017.1", + "markdown": "Reports collectors that can be simplified.\n\nIn particular, some cascaded `groupingBy()` collectors can be expressed by using a\nsimpler `toMap()` collector, which is also likely to be more performant.\n\nExample:\n\n\n Collectors.groupingByConcurrent(String::length, Collectors.collectingAndThen(Collectors.maxBy(String::compareTo), Optional::get));\n\nAfter the quick-fix is applied:\n\n\n Collectors.toConcurrentMap(String::length, Function.identity(), BinaryOperator.maxBy(String::compareTo));\n\nThis inspection only reports if the language level of the project or module is 8 or higher.\n\nNew in 2017.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SimplifyCollector", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Annotation", + "shortDescription": { + "text": "Annotation" + }, + "fullDescription": { + "text": "Reports annotations. Annotations are not supported in Java 1.4 and earlier JVM.", + "markdown": "Reports annotations. Annotations are not supported in Java 1.4 and earlier JVM." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "Annotation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level issues", + "index": 64, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MultipleTopLevelClassesInFile", + "shortDescription": { + "text": "Multiple top level classes in single file" + }, + "fullDescription": { + "text": "Reports multiple top-level classes in a single Java file. Putting multiple top-level classes in one file may be confusing and degrade the usefulness of various software tools.", + "markdown": "Reports multiple top-level classes in a single Java file.\n\nPutting multiple\ntop-level classes in one file may be confusing and degrade the usefulness of various\nsoftware tools." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MultipleTopLevelClassesInFile", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryUnicodeEscape", + "shortDescription": { + "text": "Unnecessary unicode escape sequence" + }, + "fullDescription": { + "text": "Reports unnecessary unicode escape sequences. For example, when the file encoding can handle the character without escaping it. Unicode control characters are not reported by this inspection (except for a line feed and a tab). Example: 'String s = \"\\u0062\";'", + "markdown": "Reports unnecessary unicode escape sequences. For example, when the file encoding can handle the character without escaping it. Unicode control characters are not reported by this inspection (except for a line feed and a tab).\n\n**Example:**\n\n String s = \"\\u0062\";\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryUnicodeEscape", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Internationalization", + "index": 9, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PrimitiveArrayArgumentToVariableArgMethod", + "shortDescription": { + "text": "Confusing primitive array argument to varargs method" + }, + "fullDescription": { + "text": "Reports any calls to a variable arity method where the call has a primitive array in the variable arity parameter position (for example, 'System.out.printf(\"%s\", new int[]{1, 2, 3})'). Such a primitive-array argument may be confusing, as it will be wrapped as a single-element array, rather than each individual element being boxed, as might be expected. Example: 'String.format(\"%s\", new int[]{1, 2, 3});' After the quick-fix is applied: 'String.format(\"%s\", (Object) new int[]{1, 2, 3});'", + "markdown": "Reports any calls to a variable arity method where the call has a primitive array in the variable arity parameter position (for example, `System.out.printf(\"%s\", new int[]{1, 2, 3})`). Such a primitive-array argument may be confusing, as it will be wrapped as a single-element array, rather than each individual element being boxed, as might be expected.\n\n**Example:**\n\n\n String.format(\"%s\", new int[]{1, 2, 3});\n\nAfter the quick-fix is applied:\n\n\n String.format(\"%s\", (Object) new int[]{1, 2, 3});\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "PrimitiveArrayArgumentToVarargsMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringTokenizer", + "shortDescription": { + "text": "Use of 'StringTokenizer'" + }, + "fullDescription": { + "text": "Reports usages of the 'StringTokenizer' class. Excessive use of 'StringTokenizer' is incorrect in an internationalized environment.", + "markdown": "Reports usages of the `StringTokenizer` class. Excessive use of `StringTokenizer` is incorrect in an internationalized environment." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UseOfStringTokenizer", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Internationalization", + "index": 9, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IteratorNextDoesNotThrowNoSuchElementException", + "shortDescription": { + "text": "'Iterator.next()' which can't throw 'NoSuchElementException'" + }, + "fullDescription": { + "text": "Reports implementations of 'Iterator.next()' that cannot throw 'java.util.NoSuchElementException'. Such implementations violate the contract of 'java.util.Iterator', and may result in subtle bugs if the iterator is used in a non-standard way. Example: 'class Numbers implements Iterator {\n @Override\n public Integer next() { //warning\n if (hasNext()) {\n return generateNext();\n } else {\n return null; //throw NoSuchElementException instead\n }\n }\n\n ...\n }'", + "markdown": "Reports implementations of `Iterator.next()` that cannot throw `java.util.NoSuchElementException`.\n\n\nSuch implementations violate the contract of `java.util.Iterator`,\nand may result in subtle bugs if the iterator is used in a non-standard way.\n\n**Example:**\n\n\n class Numbers implements Iterator {\n @Override\n public Integer next() { //warning\n if (hasNext()) {\n return generateNext();\n } else {\n return null; //throw NoSuchElementException instead\n }\n }\n\n ...\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "IteratorNextCanNotThrowNoSuchElementException", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IncorrectMessageFormat", + "shortDescription": { + "text": "Incorrect 'MessageFormat' pattern" + }, + "fullDescription": { + "text": "Reports incorrect message format patterns or incorrect indexes of placeholders The following errors are reported: Unparsed or negative index Unclosed brace Unpaired quote. In this case, a part of a pattern may not be used Probably incorrect number of quotes Incorrect lower bound of nested choice patterns Incorrect indexes of placeholders. In this case, a placeholder may not be substituted or an argument may not be used Examples: 'MessageFormat.format(\"{wrong}\", 1); // incorrect index\n MessageFormat.format(\"{0\", 1); // Unmatched brace\n MessageFormat.format(\"'{0}\", 1); // Unpaired quote\n MessageFormat.format(\"It''''s {0}\", 1); // \"It''s\" will be printed, instead of \"It's\"\n MessageFormat.format(\"{0}\", 1, 2); // The argument with index '1' is not used in the pattern' New in 2023.2", + "markdown": "Reports incorrect message format patterns or incorrect indexes of placeholders\n\nThe following errors are reported:\n\n* Unparsed or negative index\n* Unclosed brace\n* Unpaired quote. In this case, a part of a pattern may not be used\n* Probably incorrect number of quotes\n* Incorrect lower bound of nested choice patterns\n* Incorrect indexes of placeholders. In this case, a placeholder may not be substituted or an argument may not be used\n\nExamples:\n\n\n MessageFormat.format(\"{wrong}\", 1); // incorrect index\n MessageFormat.format(\"{0\", 1); // Unmatched brace\n MessageFormat.format(\"'{0}\", 1); // Unpaired quote\n MessageFormat.format(\"It''''s {0}\", 1); // \"It''s\" will be printed, instead of \"It's\"\n MessageFormat.format(\"{0}\", 1, 2); // The argument with index '1' is not used in the pattern\n\nNew in 2023.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "IncorrectMessageFormat", + "cweIds": [ + 628, + 707 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspiciousTernaryOperatorInVarargsCall", + "shortDescription": { + "text": "Suspicious ternary operator in varargs method call" + }, + "fullDescription": { + "text": "Reports vararg method calls that use a ternary operator with mixed array and non-array branches. When compiled, both branches are wrapped in arrays. As a result, the array branch is turned into a two-dimensional array, which may indicate a problem. The quick-fix wraps the non-array branch in an array to prevent the compiler from doing the conversion. Example: 'static void bar(boolean flag) {\n Object[] a = {1, 2};\n Object b = \"hello\";\n foo(flag ? a : b);\n }\n static void foo(Object... obj) {\n }' After the quick-fix: 'static void bar(boolean flag) {\n Object[] a = {1, 2};\n Object b = \"hello\";\n foo(flag ? a : new Object[]{b});\n }\n static void foo(Object... obj) {\n }' New in 2020.3", + "markdown": "Reports vararg method calls that use a ternary operator with mixed array and non-array branches.\n\n\nWhen compiled, both branches are wrapped in arrays. As a result, the array branch is turned into\na two-dimensional array, which may indicate a problem.\n\n\nThe quick-fix wraps the non-array branch in an array to prevent the compiler from doing the conversion.\n\n**Example:**\n\n\n static void bar(boolean flag) {\n Object[] a = {1, 2};\n Object b = \"hello\";\n foo(flag ? a : b);\n }\n static void foo(Object... obj) {\n }\n\nAfter the quick-fix:\n\n\n static void bar(boolean flag) {\n Object[] a = {1, 2};\n Object b = \"hello\";\n foo(flag ? a : new Object[]{b});\n }\n static void foo(Object... obj) {\n }\n\nNew in 2020.3" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SuspiciousTernaryOperatorInVarargsCall", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UseBulkOperation", + "shortDescription": { + "text": "Bulk operation can be used instead of iteration" + }, + "fullDescription": { + "text": "Reports single operations inside loops that could be replaced with a bulk method. Not only are bulk methods shorter, but in some cases they may be more performant as well. Example: 'void test(Collection numbers) {\n List result = new ArrayList<>();\n for (Integer i : numbers) {\n result.add(i);\n }\n }' After the fix is applied: 'void test(Collection numbers) {\n List result = new ArrayList<>();\n result.addAll(numbers);\n }' The Use Arrays.asList() to wrap arrays option allows to report arrays, even if the bulk method requires a collection. In this case the quick-fix will automatically wrap the array in 'Arrays.asList()' call. New in 2017.1", + "markdown": "Reports single operations inside loops that could be replaced with a bulk method.\n\n\nNot only are bulk methods shorter, but in some cases they may be more performant as well.\n\n**Example:**\n\n void test(Collection numbers) {\n List result = new ArrayList<>();\n for (Integer i : numbers) {\n result.add(i);\n }\n }\n\nAfter the fix is applied:\n\n\n void test(Collection numbers) {\n List result = new ArrayList<>();\n result.addAll(numbers);\n }\n\n\nThe **Use Arrays.asList() to wrap arrays** option allows to report arrays, even if the bulk method requires a collection.\nIn this case the quick-fix will automatically wrap the array in `Arrays.asList()` call.\n\nNew in 2017.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "UseBulkOperation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AccessToNonThreadSafeStaticFieldFromInstance", + "shortDescription": { + "text": "Non-thread-safe 'static' field access" + }, + "fullDescription": { + "text": "Reports access to 'static' fields that are of a non-thread-safe type. When a 'static' field is accessed from an instance method or a non-synchronized block, multiple threads can access that field. This can lead to unspecified side effects, like exceptions and incorrect results. Example: 'class Sample {\n private static final SimpleDateFormat df = new SimpleDateFormat(\"yyyy-MM-dd\");\n String method() {\n return df.format(\"\");\n }\n }' You can specify which types should be considered not thread-safe. Only fields with these exact types or initialized with these exact types are reported, because there may exist thread-safe subclasses of these types.", + "markdown": "Reports access to `static` fields that are of a non-thread-safe type.\n\n\nWhen a `static` field is accessed from an instance method or a non-synchronized block,\nmultiple threads can access that field.\nThis can lead to unspecified side effects, like exceptions and incorrect results.\n\n**Example:**\n\n\n class Sample {\n private static final SimpleDateFormat df = new SimpleDateFormat(\"yyyy-MM-dd\");\n String method() {\n return df.format(\"\");\n }\n }\n\n\nYou can specify which types should be considered not thread-safe.\nOnly fields with these exact types or initialized with these exact types are reported,\nbecause there may exist thread-safe subclasses of these types." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AccessToNonThreadSafeStaticField", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BigDecimalEquals", + "shortDescription": { + "text": "'equals()' called on 'BigDecimal'" + }, + "fullDescription": { + "text": "Reports 'equals()' calls that compare two 'java.math.BigDecimal' numbers. This is normally a mistake, as two 'java.math.BigDecimal' numbers are only equal if they are equal in both value and scale. Example: 'if (new BigDecimal(\"2.0\").equals(\n new BigDecimal(\"2.00\"))) {} // false' After the quick-fix is applied: 'if (new BigDecimal(\"2.0\").compareTo(\n new BigDecimal(\"2.00\")) == 0) {} // true'", + "markdown": "Reports `equals()` calls that compare two `java.math.BigDecimal` numbers. This is normally a mistake, as two `java.math.BigDecimal` numbers are only equal if they are equal in both value and scale.\n\n**Example:**\n\n\n if (new BigDecimal(\"2.0\").equals(\n new BigDecimal(\"2.00\"))) {} // false\n\nAfter the quick-fix is applied:\n\n\n if (new BigDecimal(\"2.0\").compareTo(\n new BigDecimal(\"2.00\")) == 0) {} // true\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "BigDecimalEquals", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AssignmentToCatchBlockParameter", + "shortDescription": { + "text": "Assignment to 'catch' block parameter" + }, + "fullDescription": { + "text": "Reports assignments to, 'catch' block parameters. Changing a 'catch' block parameter is very confusing and should be discouraged. The quick-fix adds a declaration of a new variable. Example: 'void processFile(String fileName) throws Exception {\n try {\n doProcessFile(fileName);\n } catch(Exception ex) {\n if (ex instanceof UncheckedIOException) {\n // Warning: catch block parameter reassigned\n ex = ((UncheckedIOException) ex).getCause();\n }\n throw ex;\n }\n }' After the quick-fix is applied: 'void processFile(String fileName) throws Exception {\n try {\n doProcessFile(fileName);\n } catch(Exception ex) {\n Exception unwrapped = ex;\n if (unwrapped instanceof UncheckedIOException) {\n unwrapped = ((UncheckedIOException)\n unwrapped).getCause();\n }\n throw unwrapped;\n }\n }'", + "markdown": "Reports assignments to, `catch` block parameters.\n\nChanging a `catch` block parameter is very confusing and should be discouraged.\n\nThe quick-fix adds a declaration of a new variable.\n\n**Example:**\n\n\n void processFile(String fileName) throws Exception {\n try {\n doProcessFile(fileName);\n } catch(Exception ex) {\n if (ex instanceof UncheckedIOException) {\n // Warning: catch block parameter reassigned\n ex = ((UncheckedIOException) ex).getCause();\n }\n throw ex;\n }\n }\n\nAfter the quick-fix is applied:\n\n\n void processFile(String fileName) throws Exception {\n try {\n doProcessFile(fileName);\n } catch(Exception ex) {\n Exception unwrapped = ex;\n if (unwrapped instanceof UncheckedIOException) {\n unwrapped = ((UncheckedIOException)\n unwrapped).getCause();\n }\n throw unwrapped;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AssignmentToCatchBlockParameter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Assignment issues", + "index": 34, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AbstractMethodOverridesAbstractMethod", + "shortDescription": { + "text": "Abstract method overrides abstract method" + }, + "fullDescription": { + "text": "Reports 'abstract' methods that override 'abstract' methods. Such methods don't make sense because any concrete child class will have to implement the abstract method anyway. Methods whose return types, exception declarations, annotations, or modifiers differ from the overridden method are not reported by this inspection. Configure the inspection: Use the Ignore methods with different Javadoc than their super methods option to ignore any abstract methods whose JavaDoc comment differs from their super method.", + "markdown": "Reports `abstract` methods that override `abstract` methods.\n\nSuch methods don't make sense because any concrete child class will have to implement the abstract method anyway.\n\n\nMethods whose return types, exception declarations, annotations, or modifiers differ from the overridden method are not reported by this inspection.\n\n\nConfigure the inspection:\n\n* Use the **Ignore methods with different Javadoc than their super methods** option to ignore any abstract methods whose JavaDoc comment differs from their super method." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AbstractMethodOverridesAbstractMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Inheritance issues", + "index": 25, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SerializableInnerClassHasSerialVersionUIDField", + "shortDescription": { + "text": "Serializable non-static inner class without 'serialVersionUID'" + }, + "fullDescription": { + "text": "Reports non-static inner classes that implement 'java.io.Serializable', but do not define a 'serialVersionUID' field. Without a 'serialVersionUID' field, any change to the class will make previously serialized versions unreadable. It is strongly recommended that 'Serializable' non-static inner classes have a 'serialVersionUID' field, otherwise the default serialization algorithm may result in serialized versions being incompatible between compilers due to differences in synthetic accessor methods. A quick-fix is suggested to add the missing 'serialVersionUID' field. Example: 'class Outer {\n class Inner implements Serializable {}\n }' After the quick-fix is applied: 'class Outer {\n class Inner implements Serializable {\n private static final long serialVersionUID = -7004458730436243902L;\n }\n }' Use the following options to configure the inspection: List classes whose inheritors should not be reported by this inspection. This is meant for classes that inherit 'Serializable' from a superclass but are not intended for serialization. Whether to ignore 'Serializable' anonymous classes.", + "markdown": "Reports non-static inner classes that implement `java.io.Serializable`, but do not define a `serialVersionUID` field.\n\n\nWithout a `serialVersionUID` field, any change to the class will make previously\nserialized versions unreadable. It is strongly recommended that `Serializable`\nnon-static inner classes have a `serialVersionUID` field, otherwise the default\nserialization algorithm may result in serialized versions being incompatible between\ncompilers due to differences in synthetic accessor methods.\n\n\nA quick-fix is suggested to add the missing `serialVersionUID` field.\n\n**Example:**\n\n\n class Outer {\n class Inner implements Serializable {}\n }\n\nAfter the quick-fix is applied:\n\n\n class Outer {\n class Inner implements Serializable {\n private static final long serialVersionUID = -7004458730436243902L;\n }\n }\n\nUse the following options to configure the inspection:\n\n* List classes whose inheritors should not be reported by this inspection. This is meant for classes that inherit `Serializable` from a superclass but are not intended for serialization.\n* Whether to ignore `Serializable` anonymous classes." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SerializableNonStaticInnerClassWithoutSerialVersionUID", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LoopStatementsThatDontLoop", + "shortDescription": { + "text": "Loop statement that does not loop" + }, + "fullDescription": { + "text": "Reports any instance of 'for', 'while', and 'do' statements whose bodies will be executed once at most. Normally, this is an indication of a bug. Use the Ignore enhanced for loops option to ignore the foreach loops. They are sometimes used to perform an action only on the first item of an iterable in a compact way. Example: 'for (String s : stringIterable) {\n doSomethingOnFirstString(s);\n break;\n }'", + "markdown": "Reports any instance of `for`, `while`, and `do` statements whose bodies will be executed once at most. Normally, this is an indication of a bug.\n\n\nUse the **Ignore enhanced for loops** option to ignore the foreach loops.\nThey are sometimes used to perform an action only on the first item of an iterable in a compact way.\n\nExample:\n\n\n for (String s : stringIterable) {\n doSomethingOnFirstString(s);\n break;\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "LoopStatementThatDoesntLoop", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ArrayCreationWithoutNewKeyword", + "shortDescription": { + "text": "Array creation without 'new' expression" + }, + "fullDescription": { + "text": "Reports array initializers without 'new' array expressions and suggests adding them. Example: 'int[] a = {42}' After the quick-fix is applied: 'int[] a = new int[]{42}'", + "markdown": "Reports array initializers without `new` array expressions and suggests adding them.\n\nExample:\n\n\n int[] a = {42}\n\nAfter the quick-fix is applied:\n\n\n int[] a = new int[]{42}\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ArrayCreationWithoutNewKeyword", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConfusingOctalEscape", + "shortDescription": { + "text": "Confusing octal escape sequence" + }, + "fullDescription": { + "text": "Reports string literals containing an octal escape sequence immediately followed by a digit. Such strings may be confusing, and are often the result of errors in escape code creation. Example: 'System.out.println(\"\\1234\"); // Octal escape sequence '\\123' immediately followed by a digit'", + "markdown": "Reports string literals containing an octal escape sequence immediately followed by a digit.\n\nSuch strings may be confusing, and are often the result of errors in escape code creation.\n\n**Example:**\n\n\n System.out.println(\"\\1234\"); // Octal escape sequence '\\123' immediately followed by a digit\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ConfusingOctalEscapeSequence", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ParameterNameDiffersFromOverriddenParameter", + "shortDescription": { + "text": "Parameter name differs from parameter in overridden or overloaded method" + }, + "fullDescription": { + "text": "Reports parameters whose names differ from the corresponding parameters of the methods they override or overload. While legal in Java, such inconsistent names may be confusing and decrease the documentation benefits of good naming practices. Example: 'class Person {\n Person(String fullName) {}\n }\n class Child extends Person {\n Child(String name) { super(name); }\n }' After the quick-fix is applied: 'class Person {\n Person(String fullName) {}\n }\n class Child extends Person {\n Child(String fullName) { super(fullName); }\n }' Use the options to indicate whether to ignore overridden parameter names that are only a single character long or come from a library method. Both can be useful if you do not wish to be bound by dubious naming conventions used in libraries.", + "markdown": "Reports parameters whose names differ from the corresponding parameters of the methods they override or overload. While legal in Java, such inconsistent names may be confusing and decrease the documentation benefits of good naming practices.\n\n**Example:**\n\n\n class Person {\n Person(String fullName) {}\n }\n class Child extends Person {\n Child(String name) { super(name); }\n }\n\nAfter the quick-fix is applied:\n\n\n class Person {\n Person(String fullName) {}\n }\n class Child extends Person {\n Child(String fullName) { super(fullName); }\n }\n\n\nUse the options to indicate whether to ignore overridden parameter names that are only\na single character long or come from a library method. Both can be useful if\nyou do not wish to be bound by dubious naming conventions used in libraries." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ParameterNameDiffersFromOverriddenParameter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions", + "index": 51, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OctalLiteral", + "shortDescription": { + "text": "Octal integer" + }, + "fullDescription": { + "text": "Reports octal integer literals. Some coding standards prohibit the use of octal literals, as they may be easily confused with decimal literals. Example: 'int i = 015;\n int j = 0_777;' This inspection has two different quick-fixes. After the Convert octal literal to decimal literal quick-fix is applied, the code changes to: 'int i = 13;\n int j = 511;' After the Remove leading zero to make decimal quick-fix is applied, the code changes to: 'int i = 15;\n int j = 777;'", + "markdown": "Reports octal integer literals. Some coding standards prohibit the use of octal literals, as they may be easily confused with decimal literals.\n\nExample:\n\n\n int i = 015;\n int j = 0_777;\n\nThis inspection has two different quick-fixes.\nAfter the **Convert octal literal to decimal literal** quick-fix is applied, the code changes to:\n\n\n int i = 13;\n int j = 511;\n\nAfter the **Remove leading zero to make decimal** quick-fix is applied, the code changes to:\n\n\n int i = 15;\n int j = 777;\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "OctalInteger", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LambdaParameterHidingMemberVariable", + "shortDescription": { + "text": "Lambda parameter hides field" + }, + "fullDescription": { + "text": "Reports lambda parameters named identically to a field of a surrounding class. As a result of such naming, you may accidentally use the lambda parameter when using the identically named field is intended. A quick-fix is suggested to rename the lambda parameter. Example: 'public class MyClass {\n public Object foo;\n\n void sort(List list) {\n list.sort((foo, bar) -> foo - bar);\n }\n }' Use the option to choose whether to ignore fields that are not visible from the lambda expression. For example, private fields of a superclass.", + "markdown": "Reports lambda parameters named identically to a field of a surrounding class. As a result of such naming, you may accidentally use the lambda parameter when using the identically named field is intended.\n\nA quick-fix is suggested to rename the lambda parameter.\n\n**Example:**\n\n\n public class MyClass {\n public Object foo;\n\n void sort(List list) {\n list.sort((foo, bar) -> foo - bar);\n }\n }\n\n\nUse the option to choose whether to ignore fields that are not visible from the lambda expression.\nFor example, private fields of a superclass." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LambdaParameterHidesMemberVariable", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Visibility", + "index": 57, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReadWriteStringCanBeUsed", + "shortDescription": { + "text": "'Files.readString()' or 'Files.writeString()' can be used" + }, + "fullDescription": { + "text": "Reports method calls that read or write a 'String' as bytes using 'java.nio.file.Files'. Such calls can be replaced with a call to a 'Files.readString()' or 'Files.writeString()' method introduced in Java 11. Example: 'String s = \"example\";\n Files.write(Paths.get(\"out.txt\"), s.getBytes(StandardCharsets.UTF_8), StandardOpenOption.WRITE);\n s = new String(Files.readAllBytes(Paths.get(\"in.txt\")), StandardCharsets.ISO_8859_1);' After the quick fix is applied: 'String s = \"example\";\n Files.writeString(Paths.get(\"out.txt\"), s, StandardOpenOption.WRITE);\n s = Files.readString(Paths.get(\"in.txt\"), StandardCharsets.ISO_8859_1);' New in 2018.3", + "markdown": "Reports method calls that read or write a `String` as bytes using `java.nio.file.Files`. Such calls can be replaced with a call to a `Files.readString()` or `Files.writeString()` method introduced in Java 11.\n\n**Example:**\n\n\n String s = \"example\";\n Files.write(Paths.get(\"out.txt\"), s.getBytes(StandardCharsets.UTF_8), StandardOpenOption.WRITE);\n s = new String(Files.readAllBytes(Paths.get(\"in.txt\")), StandardCharsets.ISO_8859_1);\n\nAfter the quick fix is applied:\n\n\n String s = \"example\";\n Files.writeString(Paths.get(\"out.txt\"), s, StandardOpenOption.WRITE);\n s = Files.readString(Paths.get(\"in.txt\"), StandardCharsets.ISO_8859_1);\n\nNew in 2018.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ReadWriteStringCanBeUsed", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 11", + "index": 121, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConstantMathCall", + "shortDescription": { + "text": "Constant call to 'Math'" + }, + "fullDescription": { + "text": "Reports calls to 'java.lang.Math' or 'java.lang.StrictMath' methods that can be replaced with simple compile-time constants. Example: 'double v = Math.sin(0.0);' After the quick-fix is applied: 'double v = 0.0;'", + "markdown": "Reports calls to `java.lang.Math` or `java.lang.StrictMath` methods that can be replaced with simple compile-time constants.\n\n**Example:**\n\n double v = Math.sin(0.0);\n\nAfter the quick-fix is applied:\n\n double v = 0.0;\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ConstantMathCall", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MissortedModifiers", + "shortDescription": { + "text": "Missorted modifiers" + }, + "fullDescription": { + "text": "Reports declarations whose modifiers are not in the canonical preferred order (as stated in the Java Language Specification). Example: 'class Foo {\n native public final void foo();\n }' After the quick-fix is applied: 'class Foo {\n public final native void foo();\n }' Use the inspection settings to: toggle the reporting of misplaced annotations: (annotations with 'ElementType.TYPE_USE' not directly before the type and after the modifier keywords, or other annotations not before the modifier keywords). When this option is disabled, any annotation can be positioned before or after the modifier keywords. Modifier lists with annotations in between the modifier keywords will always be reported. specify whether the 'ElementType.TYPE_USE' annotation should be positioned directly before a type, even when the annotation has other targets specified.", + "markdown": "Reports declarations whose modifiers are not in the canonical preferred order (as stated in the Java Language Specification).\n\n**Example:**\n\n\n class Foo {\n native public final void foo();\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo {\n public final native void foo();\n }\n\nUse the inspection settings to:\n\n*\n toggle the reporting of misplaced annotations:\n (annotations with `ElementType.TYPE_USE` *not* directly\n before the type and after the modifier keywords, or\n other annotations *not* before the modifier keywords).\n When this option is disabled, any annotation can be positioned before or after the modifier keywords.\n Modifier lists with annotations in between the modifier keywords will always be reported.\n\n*\n specify whether the `ElementType.TYPE_USE` annotation should be positioned directly before\n a type, even when the annotation has other targets specified." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "MissortedModifiers", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TransientFieldInNonSerializableClass", + "shortDescription": { + "text": "Transient field in non-serializable class" + }, + "fullDescription": { + "text": "Reports 'transient' fields in classes that do not implement 'java.io.Serializable'. Example: 'public class NonSerializableClass {\n private transient String password;\n }' After the quick-fix is applied: 'public class NonSerializableClass {\n private String password;\n }'", + "markdown": "Reports `transient` fields in classes that do not implement `java.io.Serializable`.\n\n**Example:**\n\n\n public class NonSerializableClass {\n private transient String password;\n }\n\nAfter the quick-fix is applied:\n\n\n public class NonSerializableClass {\n private String password;\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "TransientFieldInNonSerializableClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CyclomaticComplexity", + "shortDescription": { + "text": "Overly complex method" + }, + "fullDescription": { + "text": "Reports methods that have too many branch points. A branch point is one of the following: loop statement 'if' statement ternary expression 'catch' section expression with one or more '&&' or '||' operators inside 'switch' block with non-default branches Methods with too high cyclomatic complexity may be confusing and hard to test. Use the Method complexity limit field to specify the maximum allowed cyclomatic complexity for a method.", + "markdown": "Reports methods that have too many branch points.\n\nA branch point is one of the following:\n\n* loop statement\n* `if` statement\n* ternary expression\n* `catch` section\n* expression with one or more `&&` or `||` operators inside\n* `switch` block with non-default branches\n\nMethods with too high cyclomatic complexity may be confusing and hard to test.\n\nUse the **Method complexity limit** field to specify the maximum allowed cyclomatic complexity for a method." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OverlyComplexMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Method metrics", + "index": 95, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SlowListContainsAll", + "shortDescription": { + "text": "Call to 'list.containsAll(collection)' may have poor performance" + }, + "fullDescription": { + "text": "Reports calls to 'containsAll()' on 'java.util.List'. The time complexity of this method call is O(n·m), where n is the number of elements in the list on which the method is called, and m is the number of elements in the collection passed to the method as a parameter. When the list is large, this can be an expensive operation. The quick-fix wraps the list in 'new java.util.HashSet<>()' since the time required to create 'java.util.HashSet' from 'java.util.List' and execute 'containsAll()' on 'java.util.HashSet' is O(n+m). Example: 'public boolean check(List list, Collection collection) {\n // O(n·m) complexity\n return list.containsAll(collection);\n }' After the quick-fix is applied: 'public boolean check(List list, Collection collection) {\n // O(n+m) complexity\n return new HashSet<>(list).containsAll(collection);\n }' New in 2022.1", + "markdown": "Reports calls to `containsAll()` on `java.util.List`.\n\n\nThe time complexity of this method call is O(n·m), where n is the number of elements in the list on which\nthe method is called, and m is the number of elements in the collection passed to the method as a parameter.\nWhen the list is large, this can be an expensive operation.\n\n\nThe quick-fix wraps the list in `new java.util.HashSet<>()` since the time required to create\n`java.util.HashSet` from `java.util.List` and execute `containsAll()` on\n`java.util.HashSet` is O(n+m).\n\n**Example:**\n\n public boolean check(List list, Collection collection) {\n // O(n·m) complexity\n return list.containsAll(collection);\n }\n\nAfter the quick-fix is applied:\n\n public boolean check(List list, Collection collection) {\n // O(n+m) complexity\n return new HashSet<>(list).containsAll(collection);\n }\n\nNew in 2022.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SlowListContainsAll", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AwaitNotInLoop", + "shortDescription": { + "text": "'await()' not called in loop" + }, + "fullDescription": { + "text": "Reports 'java.util.concurrent.locks.Condition.await()' not being called inside a loop. 'await()' and related methods are normally used to suspend a thread until some condition becomes true. As the thread could have been woken up for a different reason, the condition should be checked after the 'await()' call returns. A loop is a simple way to achieve this. Example: 'void acquire(Condition released) throws InterruptedException {\n released.await();\n }' Good code should look like this: 'void acquire(Condition released) throws InterruptedException {\n while (acquired) {\n released.await();\n }\n }'", + "markdown": "Reports `java.util.concurrent.locks.Condition.await()` not being called inside a loop.\n\n\n`await()` and related methods are normally used to suspend a thread until some condition becomes true.\nAs the thread could have been woken up for a different reason,\nthe condition should be checked after the `await()` call returns.\nA loop is a simple way to achieve this.\n\n**Example:**\n\n\n void acquire(Condition released) throws InterruptedException {\n released.await();\n }\n\nGood code should look like this:\n\n\n void acquire(Condition released) throws InterruptedException {\n while (acquired) {\n released.await();\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AwaitNotInLoop", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StaticVariableUninitializedUse", + "shortDescription": { + "text": "Static field used before initialization" + }, + "fullDescription": { + "text": "Reports 'static' variables that are read before initialization. The inspection ignores equality checks with 'null'. Example: 'class Foo {\n public static int bar;\n\n public static void main(String[] args) {\n System.out.println(bar);\n }\n }' Note that this inspection uses a very conservative dataflow algorithm and may incorrectly report 'static' variables as uninitialized. Variables reported as initialized will always be initialized. Use the Ignore primitive fields option to ignore uninitialized primitive fields.", + "markdown": "Reports `static` variables that are read before initialization.\n\nThe inspection ignores equality checks with `null`.\n\n**Example:**\n\n\n class Foo {\n public static int bar;\n\n public static void main(String[] args) {\n System.out.println(bar);\n }\n }\n\nNote that this inspection uses a very conservative dataflow algorithm and may incorrectly report `static` variables as uninitialized. Variables\nreported as initialized will always be initialized.\n\nUse the **Ignore primitive fields** option to ignore uninitialized primitive fields." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StaticVariableUsedBeforeInitialization", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Initialization", + "index": 28, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CyclicClassDependency", + "shortDescription": { + "text": "Cyclic class dependency" + }, + "fullDescription": { + "text": "Reports classes that are mutually or cyclically dependent on other classes. Such cyclic dependencies make code fragile and hard to maintain. Available only from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name and isn't reported in the editor.", + "markdown": "Reports classes that are mutually or cyclically dependent on other classes.\n\nSuch cyclic dependencies make code fragile and hard to maintain.\n\nAvailable only from **Code \\| Inspect Code** or\n**Code \\| Analyze Code \\| Run Inspection by Name** and isn't reported in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CyclicClassDependency", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Dependency issues", + "index": 89, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ExceptionPackage", + "shortDescription": { + "text": "Exception package" + }, + "fullDescription": { + "text": "Reports packages that only contain classes that extend 'java.lang.Throwable', either directly or indirectly. Although exceptions usually don't depend on other classes for their implementation, they are normally not used separately. It is often a better design to locate exceptions in the same package as the classes that use them. Available only from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name and isn't reported in the editor.", + "markdown": "Reports packages that only contain classes that extend `java.lang.Throwable`, either directly or indirectly.\n\nAlthough exceptions usually don't depend on other classes for their implementation, they are normally not used separately.\nIt is often a better design to locate exceptions in the same package as the classes that use them.\n\nAvailable only from **Code \\| Inspect Code** or\n**Code \\| Analyze Code \\| Run Inspection by Name** and isn't reported in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ExceptionPackage", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Packaging issues", + "index": 36, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringBufferReplaceableByStringBuilder", + "shortDescription": { + "text": "'StringBuffer' may be 'StringBuilder'" + }, + "fullDescription": { + "text": "Reports variables declared as 'StringBuffer' and suggests replacing them with 'StringBuilder'. 'StringBuilder' is a non-thread-safe replacement for 'StringBuffer'. This inspection only reports if the language level of the project or module is 5 or higher.", + "markdown": "Reports variables declared as `StringBuffer` and suggests replacing them with `StringBuilder`. `StringBuilder` is a non-thread-safe replacement for `StringBuffer`.\n\nThis inspection only reports if the language level of the project or module is 5 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StringBufferMayBeStringBuilder", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 5", + "index": 53, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TypeParameterExtendsObject", + "shortDescription": { + "text": "Type parameter explicitly extends 'Object'" + }, + "fullDescription": { + "text": "Reports type parameters and wildcard type arguments that are explicitly declared to extend 'java.lang.Object'. Such 'extends' clauses are redundant as 'java.lang.Object' is a supertype for all classes. Example: 'class ClassA {}' If you need to preserve the 'extends Object' clause because of annotations, disable the Ignore when java.lang.Object is annotated option. This might be useful, for example, when you use a nullness analyzer, and the 'extends Object' clause holds a '@Nullable'/'@NotNull' annotation. Example: 'class MyClass {}'", + "markdown": "Reports type parameters and wildcard type arguments that are explicitly declared to extend `java.lang.Object`.\n\nSuch 'extends' clauses are redundant as `java.lang.Object` is a supertype for all classes.\n\n**Example:**\n\n class ClassA {}\n\n\nIf you need to preserve the 'extends Object' clause because of annotations, disable the\n**Ignore when java.lang.Object is annotated** option.\nThis might be useful, for example, when you use a nullness analyzer, and the 'extends Object' clause\nholds a `@Nullable`/`@NotNull` annotation.\n\n**Example:**\n\n class MyClass {}\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "TypeParameterExplicitlyExtendsObject", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SynchronizedMethod", + "shortDescription": { + "text": "'synchronized' method" + }, + "fullDescription": { + "text": "Reports the 'synchronized' modifier on methods. There are several reasons a 'synchronized' modifier on a method may be a bad idea: As little work as possible should be performed under a lock. Therefore it is often better to use a 'synchronized' block and keep there only the code that works with shared state. Synchronization becomes a part of a method's interface. This makes a transition to a different locking mechanism difficult. Keeping track of what is locking a particular object gets harder. The DoS (denial-of-service) attack becomes feasible either on purpose or unknowingly when inheriting the method's class. As an alternative, consider synchronizing on a 'private final' lock object, access to which can be completely controlled. A quick-fix is provided to wrap the method body with 'synchronized(this)'. Example: 'class Main {\n public synchronized void fooBar() {\n }\n }' After the quick-fix is applied: 'class Main {\n public void fooBar() {\n synchronized (this) {\n }\n }\n }' You can configure the following options for this inspection: Include native methods - include native methods into the inspection's scope. Ignore methods overriding a synchronized method - do not report methods that override a 'synchronized' method.", + "markdown": "Reports the `synchronized` modifier on methods.\n\n\nThere are several reasons a `synchronized` modifier on a method may be a bad idea:\n\n1. As little work as possible should be performed under a lock. Therefore it is often better to use a `synchronized` block and keep there only the code that works with shared state.\n2. Synchronization becomes a part of a method's interface. This makes a transition to a different locking mechanism difficult.\n3. Keeping track of what is locking a particular object gets harder.\n4. The DoS (denial-of-service) attack becomes feasible either on purpose or unknowingly when inheriting the method's class.\n\n\nAs an alternative, consider synchronizing on a `private final` lock object, access to which can be completely controlled.\n\nA quick-fix is provided to wrap the method body with `synchronized(this)`.\n\n**Example:**\n\n\n class Main {\n public synchronized void fooBar() {\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Main {\n public void fooBar() {\n synchronized (this) {\n }\n }\n }\n\nYou can configure the following options for this inspection:\n\n1. **Include native methods** - include native methods into the inspection's scope.\n2. **Ignore methods overriding a synchronized method** - do not report methods that override a `synchronized` method." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SynchronizedMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringConcatenationInsideStringBufferAppend", + "shortDescription": { + "text": "String concatenation as argument to 'StringBuilder.append()' call" + }, + "fullDescription": { + "text": "Reports 'String' concatenation used as the argument to 'StringBuffer.append()', 'StringBuilder.append()' or 'Appendable.append()'. Such calls may profitably be turned into chained append calls on the existing 'StringBuffer/Builder/Appendable' saving the cost of an extra 'StringBuffer/Builder' allocation. This inspection ignores compile-time evaluated 'String' concatenations, in which case the conversion would only worsen performance. Example: 'void bar(StringBuilder builder, String name) {\n builder.append(\"Hello,\" + name); //warning\n builder.append(\"Hello,\" + \"world\"); //no warning\n }'", + "markdown": "Reports `String` concatenation used as the argument to `StringBuffer.append()`, `StringBuilder.append()` or `Appendable.append()`.\n\n\nSuch calls may profitably be turned into chained append calls on the existing `StringBuffer/Builder/Appendable`\nsaving the cost of an extra `StringBuffer/Builder` allocation.\nThis inspection ignores compile-time evaluated `String` concatenations, in which case the conversion would only\nworsen performance.\n\n**Example:**\n\n\n void bar(StringBuilder builder, String name) {\n builder.append(\"Hello,\" + name); //warning\n builder.append(\"Hello,\" + \"world\"); //no warning\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "StringConcatenationInsideStringBufferAppend", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AbstractMethodWithMissingImplementations", + "shortDescription": { + "text": "Abstract method with missing implementations" + }, + "fullDescription": { + "text": "Reports 'abstract' methods that are not implemented in every concrete subclass. This results in a compile-time error on the subclasses; the inspection reports the problem at the point of the abstract method, allowing faster detection of the problem.", + "markdown": "Reports `abstract` methods that are not implemented in every concrete subclass.\n\n\nThis results in a compile-time error on the subclasses;\nthe inspection reports the problem at the point of the abstract method, allowing faster detection of the problem." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AbstractMethodWithMissingImplementations", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Inheritance issues", + "index": 25, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FeatureEnvy", + "shortDescription": { + "text": "Feature envy" + }, + "fullDescription": { + "text": "Reports the Feature Envy code smell. The warning is thrown when a method calls methods on another class three or more times. Calls to library classes, parent classes, contained or containing classes are not counted by this inspection. Feature envy is often an indication of the fact that this functionality is located in a wrong class. Example: 'class JobManager {\n // Warning: this method calls three methods\n // of the Job class\n // It would be better to move this chain of\n // calls to the Job class itself.\n void performJob(Job job) {\n job.beforeStart();\n job.process();\n job.afterProcessing();\n }\n }'", + "markdown": "Reports the *Feature Envy* code smell. The warning is thrown when a method calls methods on another class three or more times. Calls to library classes, parent classes, contained or containing classes are not counted by this inspection. Feature envy is often an indication of the fact that this functionality is located in a wrong class.\n\nExample:\n\n\n class JobManager {\n // Warning: this method calls three methods\n // of the Job class\n // It would be better to move this chain of\n // calls to the Job class itself.\n void performJob(Job job) {\n job.beforeStart();\n job.process();\n job.afterProcessing();\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "FeatureEnvy", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Abstraction issues", + "index": 78, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BoxingBoxedValue", + "shortDescription": { + "text": "Boxing of already boxed value" + }, + "fullDescription": { + "text": "Reports boxing of already boxed values. This is a redundant operation since any boxed value will first be auto-unboxed before boxing the value again. If done inside an inner loop, such code may cause performance problems. Example: 'Integer value = 1;\n method(Integer.valueOf(value));' After the quick fix is applied: 'Integer value = 1;\n method(value);'", + "markdown": "Reports boxing of already boxed values.\n\n\nThis is a redundant\noperation since any boxed value will first be auto-unboxed before boxing the\nvalue again. If done inside an inner loop, such code may cause performance\nproblems.\n\n**Example:**\n\n\n Integer value = 1;\n method(Integer.valueOf(value));\n\nAfter the quick fix is applied:\n\n\n Integer value = 1;\n method(value);\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "BoxingBoxedValue", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Convert2MethodRef", + "shortDescription": { + "text": "Lambda can be replaced with method reference" + }, + "fullDescription": { + "text": "Reports lambdas that can be replaced with method references. While often it could be a matter of taste, method references are more clear and readable compared to lambdas. Example: 'Runnable r = () -> System.out.println();' After the quick-fix is applied: 'Runnable r = System.out::println;' The inspection may suggest method references even if a lambda doesn't call any method, like replacing 'obj -> obj != null' with 'Objects::nonNull'. Use the Settings | Editor | Code Style | Java | Code Generation settings to configure special method references. This inspection only reports if the language level of the project or module is 8 or higher.", + "markdown": "Reports lambdas that can be replaced with method references. While often it could be a matter of taste, method references are more clear and readable compared to lambdas.\n\nExample:\n\n\n Runnable r = () -> System.out.println();\n\nAfter the quick-fix is applied:\n\n\n Runnable r = System.out::println;\n\n\nThe inspection may suggest method references even if a lambda doesn't call any method, like replacing `obj -> obj != null`\nwith `Objects::nonNull`.\nUse the [Settings \\| Editor \\| Code Style \\| Java \\| Code Generation](settings://preferences.sourceCode.Java?Lambda%20Body)\nsettings to configure special method references.\n\nThis inspection only reports if the language level of the project or module is 8 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "Convert2MethodRef", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 8", + "index": 62, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantCollectionOperation", + "shortDescription": { + "text": "Redundant 'Collection' operation" + }, + "fullDescription": { + "text": "Reports unnecessarily complex collection operations which have simpler alternatives. Example: 'void f(String[] array, Collection collection) {\n String[] strings = Arrays.asList(array).subList(0, 10).toArray(new String[0]);\n boolean contains = collection.containsAll(Collections.singletonList(\"x\"));\n }' After the quick-fix is applied: 'void f(String[] array, Collection collection) {\n String[] strings = Arrays.copyOf(array, 10);\n boolean contains = collection.contains(\"x\");\n }' New in 2018.1", + "markdown": "Reports unnecessarily complex collection operations which have simpler alternatives.\n\nExample:\n\n\n void f(String[] array, Collection collection) {\n String[] strings = Arrays.asList(array).subList(0, 10).toArray(new String[0]);\n boolean contains = collection.containsAll(Collections.singletonList(\"x\"));\n }\n\nAfter the quick-fix is applied:\n\n\n void f(String[] array, Collection collection) {\n String[] strings = Arrays.copyOf(array, 10);\n boolean contains = collection.contains(\"x\");\n }\n\nNew in 2018.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantCollectionOperation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassOnlyUsedInOnePackage", + "shortDescription": { + "text": "Class only used from one other package" + }, + "fullDescription": { + "text": "Reports classes that don't depend on any other class in their package, depend on classes from another package, and are themselves a dependency only for classes from this other package. Consider moving such classes to the package on which they depend. Available only from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name and isn't reported in the editor.", + "markdown": "Reports classes that don't depend on any other class in their package, depend on classes from another package, and are themselves a dependency only for classes from this other package. Consider moving such classes to the package on which they depend.\n\nAvailable only from **Code \\| Inspect Code** or\n**Code \\| Analyze Code \\| Run Inspection by Name** and isn't reported in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassOnlyUsedInOnePackage", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Packaging issues", + "index": 36, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryToStringCall", + "shortDescription": { + "text": "Unnecessary call to 'toString()'" + }, + "fullDescription": { + "text": "Reports calls to 'toString()' that are used in the following cases: In string concatenations In the 'java.lang.StringBuilder#append()' or 'java.lang.StringBuffer#append()' methods In the methods of 'java.io.PrintWriter' or 'java.io.PrintStream' in the methods 'org.slf4j.Logger' In these cases, conversion to string will be handled by the underlying library methods, and the explicit call to 'toString()' is not needed. Example: 'System.out.println(this.toString())' After the quick-fix is applied: 'System.out.println(this)' Note that without the 'toString()' call, the code semantics might be different: if the expression is null, then the 'null' string will be used instead of throwing a 'NullPointerException'. Use the Report only when qualifier is known to be not-null option to avoid warnings for the values that could potentially be null.", + "markdown": "Reports calls to `toString()` that are used in the following cases:\n\n* In string concatenations\n* In the `java.lang.StringBuilder#append()` or `java.lang.StringBuffer#append()` methods\n* In the methods of `java.io.PrintWriter` or `java.io.PrintStream`\n* in the methods `org.slf4j.Logger`\n\nIn these cases, conversion to string will be handled by the underlying library methods, and the explicit call to `toString()` is not needed.\n\nExample:\n\n\n System.out.println(this.toString())\n\nAfter the quick-fix is applied:\n\n\n System.out.println(this)\n\n\nNote that without the `toString()` call, the code semantics might be different: if the expression is null,\nthen the `null` string will be used instead of throwing a `NullPointerException`.\n\nUse the **Report only when qualifier is known to be not-null** option to avoid warnings for the values that could potentially be null." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryToStringCall", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuppressionAnnotation", + "shortDescription": { + "text": "Inspection suppression annotation" + }, + "fullDescription": { + "text": "Reports comments or annotations suppressing inspections. This inspection can be useful when leaving suppressions intentionally for further review. Example: '@SuppressWarnings(\"unused\")\n static Stream stringProvider() {\n return Stream.of(\"foo\", \"bar\");\n }'", + "markdown": "Reports comments or annotations suppressing inspections.\n\nThis inspection can be useful when leaving suppressions intentionally for further review.\n\n**Example:**\n\n\n @SuppressWarnings(\"unused\")\n static Stream stringProvider() {\n return Stream.of(\"foo\", \"bar\");\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SuppressionAnnotation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code maturity", + "index": 50, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OverriddenMethodCallDuringObjectConstruction", + "shortDescription": { + "text": "Overridden method called during object construction" + }, + "fullDescription": { + "text": "Reports any calls to overridden methods of the current class during object construction. This happens if an object construction is inside: A constructor A non-static instance initializer A non-static field initializer 'clone()' 'readObject()' 'readObjectNoData()' Such calls may result in subtle bugs, as the object is not guaranteed to be initialized before the method call occurs. Example: 'abstract class Parent {\n void someMethod() { }\n }\n\n class Child extends Parent {\n Child() {\n someMethod();\n }\n\n @Override\n void someMethod() { }\n }' This inspection shares its functionality with: The Abstract method called during object construction inspection The Overridable method called during object construction inspection Only one inspection should be enabled at the same time to prevent duplicate warnings.", + "markdown": "Reports any calls to overridden methods of the current class during object construction. This happens if an object construction is inside:\n\n* A constructor\n* A non-static instance initializer\n* A non-static field initializer\n* `clone()`\n* `readObject()`\n* `readObjectNoData()`\n\nSuch calls may result in subtle bugs, as the object is not guaranteed to be initialized before the method call occurs.\n\nExample:\n\n\n abstract class Parent {\n void someMethod() { }\n }\n\n class Child extends Parent {\n Child() {\n someMethod();\n }\n\n @Override\n void someMethod() { }\n }\n\nThis inspection shares its functionality with:\n\n* The **Abstract method called during object construction** inspection\n* The **Overridable method called during object construction** inspection\n\nOnly one inspection should be enabled at the same time to prevent duplicate warnings." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OverriddenMethodCallDuringObjectConstruction", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Initialization", + "index": 28, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AbstractClassWithoutAbstractMethods", + "shortDescription": { + "text": "Abstract class without 'abstract' methods" + }, + "fullDescription": { + "text": "Reports 'abstract' classes that have no 'abstract' methods. In most cases it does not make sense to have an 'abstract' class without any 'abstract' methods, and the 'abstract' modifier can be removed from the class. If the class was declared 'abstract' to prevent instantiation, it is often a better option to use a 'private' constructor to prevent instantiation instead. Example: 'abstract class Example {\n public String getName() {\n return \"IntelliJ IDEA\";\n }\n }' Use the option to ignore utility classes.", + "markdown": "Reports `abstract` classes that have no `abstract` methods. In most cases it does not make sense to have an `abstract` class without any `abstract` methods, and the `abstract` modifier can be removed from the class. If the class was declared `abstract` to prevent instantiation, it is often a better option to use a `private` constructor to prevent instantiation instead.\n\n**Example:**\n\n\n abstract class Example {\n public String getName() {\n return \"IntelliJ IDEA\";\n }\n }\n\nUse the option to ignore utility classes." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AbstractClassWithoutAbstractMethods", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Inheritance issues", + "index": 25, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SynchronizeOnNonFinalField", + "shortDescription": { + "text": "Synchronization on a non-final field" + }, + "fullDescription": { + "text": "Reports 'synchronized' statement lock expressions that consist of a non-'final' field reference. Such statements are unlikely to have useful semantics, as different threads may acquire different locks even when operating on the same object. Example: 'private Object o;\n public void foo() {\n synchronized (o) // synchronization on a non-final field\n { }\n }'", + "markdown": "Reports `synchronized` statement lock expressions that consist of a non-`final` field reference. Such statements are unlikely to have useful semantics, as different threads may acquire different locks even when operating on the same object.\n\n**Example:**\n\n\n private Object o;\n public void foo() {\n synchronized (o) // synchronization on a non-final field\n { }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SynchronizeOnNonFinalField", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CastThatLosesPrecision", + "shortDescription": { + "text": "Numeric cast that loses precision" + }, + "fullDescription": { + "text": "Reports cast operations between primitive numeric types that may result in precision loss. Such casts are not necessarily a problem but may result in difficult to trace bugs if the loss of precision is unexpected. Example: 'int a = 420;\n byte b = (byte) a;' Use the Ignore casts from int to char option to ignore casts from 'int' to 'char'. This type of cast is often used when implementing I/O operations because the 'read()' method of the 'java.io.Reader' class returns an 'int'. Use the Ignore casts from int 128-255 to byte option to ignore casts of constant values (128-255) from 'int' to 'byte'. Such values will overflow to negative numbers that still fit inside a byte.", + "markdown": "Reports cast operations between primitive numeric types that may result in precision loss.\n\nSuch casts are not necessarily a problem but may result in difficult to\ntrace bugs if the loss of precision is unexpected.\n\n**Example:**\n\n\n int a = 420;\n byte b = (byte) a;\n\nUse the **Ignore casts from int to char** option to ignore casts from `int` to `char`.\nThis type of cast is often used when implementing I/O operations because the `read()` method of the\n`java.io.Reader` class returns an `int`.\n\nUse the **Ignore casts from int 128-255 to byte** option to ignore casts of constant values (128-255) from `int` to\n`byte`.\nSuch values will overflow to negative numbers that still fit inside a byte." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NumericCastThatLosesPrecision", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues/Cast", + "index": 102, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReturnSeparatedFromComputation", + "shortDescription": { + "text": "'return' separated from the result computation" + }, + "fullDescription": { + "text": "Reports 'return' statements that return a local variable where the value of the variable is computed somewhere else within the same method. The quick-fix inlines the returned variable by moving the return statement to the location in which the value of the variable is computed. When the returned value can't be inlined into the 'return' statement, the quick-fix attempts to move the return statement as close to the computation of the returned value as possible. Example: 'int n = -1;\n for (int i = 0; i < a.length; i++) {\n if (a[i] == b) {\n n = i;\n break;\n }\n }\n return n;' After the quick-fix is applied: 'int n = -1;\n for (int i = 0; i < a.length; i++) {\n if (a[i] == b) {\n return i;\n }\n }\n return n;'", + "markdown": "Reports `return` statements that return a local variable where the value of the variable is computed somewhere else within the same method.\n\nThe quick-fix inlines the returned variable by moving the return statement to the location in which the value\nof the variable is computed.\nWhen the returned value can't be inlined into the `return` statement,\nthe quick-fix attempts to move the return statement as close to the computation of the returned value as possible.\n\nExample:\n\n\n int n = -1;\n for (int i = 0; i < a.length; i++) {\n if (a[i] == b) {\n n = i;\n break;\n }\n }\n return n;\n\nAfter the quick-fix is applied:\n\n\n int n = -1;\n for (int i = 0; i < a.length; i++) {\n if (a[i] == b) {\n return i;\n }\n }\n return n;\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReturnSeparatedFromComputation", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SameReturnValue", + "shortDescription": { + "text": "Method always returns the same value" + }, + "fullDescription": { + "text": "Reports methods and method hierarchies that always return the same constant. The inspection works differently in batch-mode (from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name) and on-the-fly in the editor: In batch-mode, the inspection reports methods and method hierarchies that always return the same constant. In the editor, the inspection only reports methods that have more than one 'return' statement, do not have super methods, and cannot be overridden. If a method overrides or implements a method, a contract may require it to return a specific constant, but at the same time, we may want to have several exit points. If a method can be overridden, it is possible that a different value will be returned in subclasses. Example: 'class X {\n // Warn only in batch-mode:\n int xxx() { // Method 'xxx()' and all its overriding methods always return '0'\n return 0;\n }\n }\n\n class Y extends X {\n @Override\n int xxx() {\n return 0;\n }\n\n // Warn only in batch-mode:\n int yyy() { // Method 'yyy()' always returns '0'\n return 0;\n }\n\n // Warn both in batch-mode and on-the-fly:\n final int zzz(boolean flag) { // Method 'zzz()' always returns '0'\n if (Math.random() > 0.5) {\n return 0;\n }\n return 0;\n }\n }'", + "markdown": "Reports methods and method hierarchies that always return the same constant.\n\n\nThe inspection works differently in batch-mode\n(from **Code \\| Inspect Code** or **Code \\| Analyze Code \\| Run Inspection by Name**)\nand on-the-fly in the editor:\n\n* In batch-mode, the inspection reports methods and method hierarchies that always return the same constant.\n* In the editor, the inspection only reports methods that have more than one `return` statement, do not have super methods, and cannot be overridden. If a method overrides or implements a method, a contract may require it to return a specific constant, but at the same time, we may want to have several exit points. If a method can be overridden, it is possible that a different value will be returned in subclasses.\n\n**Example:**\n\n\n class X {\n // Warn only in batch-mode:\n int xxx() { // Method 'xxx()' and all its overriding methods always return '0'\n return 0;\n }\n }\n\n class Y extends X {\n @Override\n int xxx() {\n return 0;\n }\n\n // Warn only in batch-mode:\n int yyy() { // Method 'yyy()' always returns '0'\n return 0;\n }\n\n // Warn both in batch-mode and on-the-fly:\n final int zzz(boolean flag) { // Method 'zzz()' always returns '0'\n if (Math.random() > 0.5) {\n return 0;\n }\n return 0;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SameReturnValue", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ThrowablePrintStackTrace", + "shortDescription": { + "text": "Call to 'printStackTrace()'" + }, + "fullDescription": { + "text": "Reports calls to 'Throwable.printStackTrace()' without arguments. Such statements are often used for temporary debugging and should be either removed from the production code or replaced with a more robust logging facility.", + "markdown": "Reports calls to `Throwable.printStackTrace()` without arguments.\n\nSuch statements are often used for temporary debugging and should be either removed from the production code\nor replaced with a more robust logging facility." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToPrintStackTrace", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code maturity", + "index": 50, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringBufferMustHaveInitialCapacity", + "shortDescription": { + "text": "'StringBuilder' without initial capacity" + }, + "fullDescription": { + "text": "Reports attempts to instantiate a new 'StringBuffer' or 'StringBuilder' object without specifying its initial capacity. If no initial capacity is specified, a default capacity is used, which will rarely be optimal. Failing to specify the initial capacity for 'StringBuffer' may result in performance issues if space needs to be reallocated and memory copied when the initial capacity is exceeded. Example: '// Capacity is not specified\n var sb = new StringBuilder();'", + "markdown": "Reports attempts to instantiate a new `StringBuffer` or `StringBuilder` object without specifying its initial capacity.\n\n\nIf no initial capacity is specified, a default capacity is used, which will rarely be optimal.\nFailing to specify the initial capacity for `StringBuffer` may result\nin performance issues if space needs to be reallocated and memory copied\nwhen the initial capacity is exceeded.\n\nExample:\n\n\n // Capacity is not specified\n var sb = new StringBuilder();\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StringBufferWithoutInitialCapacity", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ArrayObjectsEquals", + "shortDescription": { + "text": "Use of shallow or 'Objects' methods with arrays" + }, + "fullDescription": { + "text": "Reports expressions that seem to use an inappropriate method for determining array equality or calculating their hashcode. The following method calls are reported: 'Object.equals()' for any arrays 'Arrays.equals()' for multidimensional arrays 'Arrays.hashCode()' for multidimensional arrays", + "markdown": "Reports expressions that seem to use an inappropriate method for determining array equality or calculating their hashcode.\n\nThe following method calls are reported:\n\n* `Object.equals()` for any arrays\n* `Arrays.equals()` for multidimensional arrays\n* `Arrays.hashCode()` for multidimensional arrays" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ArrayObjectsEquals", + "cweIds": [ + 480 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ThreadWithDefaultRunMethod", + "shortDescription": { + "text": "Instantiating a 'Thread' with default 'run()' method" + }, + "fullDescription": { + "text": "Reports instantiations of 'Thread' or an inheritor without specifying a 'Runnable' parameter or overriding the 'run()' method. Such threads do nothing useful. Example: 'new Thread().start();'", + "markdown": "Reports instantiations of `Thread` or an inheritor without specifying a `Runnable` parameter or overriding the `run()` method. Such threads do nothing useful.\n\n**Example:**\n\n\n new Thread().start();\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "InstantiatingAThreadWithDefaultRunMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ThreeNegationsPerMethod", + "shortDescription": { + "text": "Method with more than three negations" + }, + "fullDescription": { + "text": "Reports methods with three or more negations. Such methods may be confusing. Example: 'void doSmth(int a, int b, boolean flag1, boolean flag2) {\n if (!flag && !flag2) {\n if (a != b) {\n doOther();\n }\n }\n }' Without negations, the method becomes easier to understand: 'void doSmth(int a, int b, boolean flag1, boolean flag2) {\n if (flag1 || flag2 || a == b) return;\n doOther();\n }' Configure the inspection: Use the Ignore negations in 'equals()' methods option to disable the inspection within 'equals()' methods. Use the Ignore negations in 'assert' statements to disable the inspection within 'assert' statements.", + "markdown": "Reports methods with three or more negations. Such methods may be confusing.\n\n**Example:**\n\n\n void doSmth(int a, int b, boolean flag1, boolean flag2) {\n if (!flag && !flag2) {\n if (a != b) {\n doOther();\n }\n }\n }\n\nWithout negations, the method becomes easier to understand:\n\n\n void doSmth(int a, int b, boolean flag1, boolean flag2) {\n if (flag1 || flag2 || a == b) return;\n doOther();\n }\n\nConfigure the inspection:\n\n* Use the **Ignore negations in 'equals()' methods** option to disable the inspection within `equals()` methods.\n* Use the **Ignore negations in 'assert' statements** to disable the inspection within `assert` statements." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MethodWithMoreThanThreeNegations", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Method metrics", + "index": 95, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java8MapApi", + "shortDescription": { + "text": "Simplifiable 'Map' operations" + }, + "fullDescription": { + "text": "Reports common usage patterns of 'java.util.Map' and suggests replacing them with: 'getOrDefault()', 'computeIfAbsent()', 'putIfAbsent()', 'merge()', or 'replaceAll()'. Example: 'map.containsKey(key) ? map.get(key) : \"default\";' After the quick-fix is applied: 'map.getOrDefault(key, \"default\");' Example: 'List list = map.get(key);\n if (list == null) {\n list = new ArrayList<>();\n map.put(key, list);\n }' After the quick-fix is applied: 'map.computeIfAbsent(key, localKey -> new ArrayList<>());' Example: 'Integer val = map.get(key);\n if (val == null) map.put(key, 1);\n else map.put(key, val + 1);' After the quick-fix is applied: 'map.merge(key, 1, (localKey, localValue) -> localValue + 1);' Example: 'for (Map.Entry entry : map.entrySet()) {\n map.put(entry.getKey(), transform(entry.getValue()));\n }' After the quick-fix is applied: 'map.replaceAll((localKey, localValue) -> transform(localValue));' Note that the replacement with 'computeIfAbsent()' or 'merge()' might work incorrectly for some 'Map' implementations if the code extracted to the lambda expression modifies the same 'Map'. By default, the warning doesn't appear if this code might have side effects. If necessary, enable the Suggest replacement even if lambda may have side effects option to always show the warning. Also, due to different handling of the 'null' value in old methods like 'put()' and newer methods like 'computeIfAbsent()' or 'merge()', semantics might change if storing the 'null' value into given 'Map' is important. The inspection won't suggest the replacement when the value is statically known to be nullable, but for values with unknown nullability the replacement is still suggested. In these cases, we recommended suppressing the warning and adding an explanatory comment. This inspection reports only if the language level of the project or module is 8 or higher.", + "markdown": "Reports common usage patterns of `java.util.Map` and suggests replacing them with: `getOrDefault()`, `computeIfAbsent()`, `putIfAbsent()`, `merge()`, or `replaceAll()`.\n\nExample:\n\n\n map.containsKey(key) ? map.get(key) : \"default\";\n\nAfter the quick-fix is applied:\n\n\n map.getOrDefault(key, \"default\");\n\nExample:\n\n\n List list = map.get(key);\n if (list == null) {\n list = new ArrayList<>();\n map.put(key, list);\n }\n\nAfter the quick-fix is applied:\n\n\n map.computeIfAbsent(key, localKey -> new ArrayList<>());\n\nExample:\n\n\n Integer val = map.get(key);\n if (val == null) map.put(key, 1);\n else map.put(key, val + 1);\n\nAfter the quick-fix is applied:\n\n\n map.merge(key, 1, (localKey, localValue) -> localValue + 1);\n\nExample:\n\n\n for (Map.Entry entry : map.entrySet()) {\n map.put(entry.getKey(), transform(entry.getValue()));\n }\n\nAfter the quick-fix is applied:\n\n\n map.replaceAll((localKey, localValue) -> transform(localValue));\n\nNote that the replacement with `computeIfAbsent()` or `merge()` might work incorrectly for some `Map`\nimplementations if the code extracted to the lambda expression modifies the same `Map`. By default,\nthe warning doesn't appear if this code might have side effects. If necessary, enable the\n**Suggest replacement even if lambda may have side effects** option to always show the warning.\n\nAlso, due to different handling of the `null` value in old methods like `put()` and newer methods like\n`computeIfAbsent()` or `merge()`, semantics might change if storing the `null` value into given\n`Map` is important. The inspection won't suggest the replacement when the value is statically known to be nullable,\nbut for values with unknown nullability the replacement is still suggested. In these cases, we recommended suppressing the warning\nand adding an explanatory comment.\n\nThis inspection reports only if the language level of the project or module is 8 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "Java8MapApi", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 8", + "index": 62, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TestOnlyProblems", + "shortDescription": { + "text": "Test-only usage in production code" + }, + "fullDescription": { + "text": "Reports '@TestOnly'- and '@VisibleForTesting'-annotated methods and classes that are used in production code. Also reports usage of applying '@TestOnly' '@VisibleForTesting' to the same element. The problems are not reported if such method or class is referenced from: Code under the Test Sources folder A test class (JUnit/TestNG) Another '@TestOnly'-annotated method Example (in production code): '@TestOnly\n fun foo() { ... }\n\n fun main () {\n foo()\n }'", + "markdown": "Reports `@TestOnly`- and `@VisibleForTesting`-annotated methods and classes that are used in production code. Also reports usage of applying `@TestOnly` `@VisibleForTesting` to the same element.\n\nThe problems are not reported if such method or class is referenced from:\n\n* Code under the **Test Sources** folder\n* A test class (JUnit/TestNG)\n* Another `@TestOnly`-annotated method\n\n**Example (in production code):**\n\n\n @TestOnly\n fun foo() { ... }\n\n fun main () {\n foo()\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "TestOnlyProblems", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages/Test frameworks", + "index": 94, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MeaninglessRecordAnnotationInspection", + "shortDescription": { + "text": "Meaningless record annotation" + }, + "fullDescription": { + "text": "Reports annotations used on record components that have no effect. This can happen in two cases: The reported annotation has the METHOD target, but the corresponding accessor is explicitly defined. The reported annotation has the PARAMETER target, but the canonical constructor is explicitly defined. Example: '@Target(ElementType.METHOD)\n@interface A { }\n \n// The annotation will not appear in bytecode at all,\n// as it should be propagated to the accessor but accessor is explicitly defined \nrecord R(@A int x) {\n public int x() { return x; }\n}' New in 2021.1", + "markdown": "Reports annotations used on record components that have no effect.\n\nThis can happen in two cases:\n\n* The reported annotation has the METHOD target, but the corresponding accessor is explicitly defined.\n* The reported annotation has the PARAMETER target, but the canonical constructor is explicitly defined.\n\nExample:\n\n\n @Target(ElementType.METHOD)\n @interface A { }\n \n // The annotation will not appear in bytecode at all,\n // as it should be propagated to the accessor but accessor is explicitly defined \n record R(@A int x) {\n public int x() { return x; }\n }\n\nNew in 2021.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "MeaninglessRecordAnnotationInspection", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JUnit5Converter", + "shortDescription": { + "text": "JUnit 4 test can be JUnit 5" + }, + "fullDescription": { + "text": "Reports JUnit 4 tests that can be automatically migrated to JUnit 5. While default runners are automatically convertible, custom runners, method- and field- rules are not and require manual changes. Example: 'import org.junit.Assert;\n import org.junit.Test;\n\n public class RelevantTest {\n @Test\n public void testIt() {\n Assert.assertEquals(\"expected\", \"actual\");\n }\n }' After the quick-fix is applied: 'import org.junit.jupiter.api.Assertions;\n import org.junit.jupiter.api.Test;\n\n public class RelevantTest {\n @Test\n public void testIt() {\n Assertions.assertEquals(\"expected\", \"actual\");\n }\n }' This inspection requires that the JUnit 5 library is available in the classpath, and JDK 1.8 or later is configured for the project.", + "markdown": "Reports JUnit 4 tests that can be automatically migrated to JUnit 5. While default runners are automatically convertible, custom runners, method- and field- rules are not and require manual changes.\n\n**Example:**\n\n\n import org.junit.Assert;\n import org.junit.Test;\n\n public class RelevantTest {\n @Test\n public void testIt() {\n Assert.assertEquals(\"expected\", \"actual\");\n }\n }\n\nAfter the quick-fix is applied:\n\n\n import org.junit.jupiter.api.Assertions;\n import org.junit.jupiter.api.Test;\n\n public class RelevantTest {\n @Test\n public void testIt() {\n Assertions.assertEquals(\"expected\", \"actual\");\n }\n }\n\nThis inspection requires that the JUnit 5 library is available in the classpath, and JDK 1.8 or later is configured for the project." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "JUnit5Converter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages/Test frameworks", + "index": 94, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EqualsWhichDoesntCheckParameterClass", + "shortDescription": { + "text": "'equals()' method which does not check class of parameter" + }, + "fullDescription": { + "text": "Reports 'equals()' methods that do not check the type of their parameter. Failure to check the type of the parameter in the 'equals()' method may result in latent errors if the object is used in an untyped collection. Example: 'class MyClass {\n int x;\n\n @Override\n public boolean equals(Object obj) {\n // equals method should return false if obj is not MyClass\n return ((MyClass)obj).x == x;\n }\n }'", + "markdown": "Reports `equals()` methods that do not check the type of their parameter.\n\nFailure to check the type of the parameter\nin the `equals()` method may result in latent errors if the object is used in an untyped collection.\n\n**Example:**\n\n\n class MyClass {\n int x;\n\n @Override\n public boolean equals(Object obj) {\n // equals method should return false if obj is not MyClass\n return ((MyClass)obj).x == x;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "EqualsWhichDoesntCheckParameterClass", + "cweIds": [ + 697 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NumericOverflow", + "shortDescription": { + "text": "Numeric overflow" + }, + "fullDescription": { + "text": "Reports expressions that overflow during computation. Usually, this happens by accident and indicates a bug. For example, a wrong type is used or a shift should be done in an opposite direction . Examples: 'float a = 1.0f/0.0f;\n long b = 30 * 24 * 60 * 60 * 1000;\n long c = 1000L << 62;'", + "markdown": "Reports expressions that overflow during computation. Usually, this happens by accident and indicates a bug. For example, a wrong type is used or a shift should be done in an opposite direction .\n\n**Examples:**\n\n\n float a = 1.0f/0.0f;\n long b = 30 * 24 * 60 * 60 * 1000;\n long c = 1000L << 62;\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "NumericOverflow", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspiciousIndentAfterControlStatement", + "shortDescription": { + "text": "Suspicious indentation after control statement without braces" + }, + "fullDescription": { + "text": "Reports suspicious indentation of statements after a control statement without braces. Such indentation can make it look like the statement is inside the control statement, when in fact it will be executed unconditionally after the control statement. Example: 'class Bar {\n void foo(int i) {\n if (i == 0)\n System.out.println(\"foo\");\n System.out.println(\"bar\"); // warning\n if (i == 1);\n System.out.println(\"great\"); // warning\n if (i == 42)\n System.out.println(\"answer\");\n System.out.println(\"question\"); // warning\n }\n }'", + "markdown": "Reports suspicious indentation of statements after a control statement without braces.\n\n\nSuch indentation can make it look like the statement is inside the control statement,\nwhen in fact it will be executed unconditionally after the control statement.\n\n**Example:**\n\n\n class Bar {\n void foo(int i) {\n if (i == 0)\n System.out.println(\"foo\");\n System.out.println(\"bar\"); // warning\n if (i == 1);\n System.out.println(\"great\"); // warning\n if (i == 42)\n System.out.println(\"answer\");\n System.out.println(\"question\"); // warning\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SuspiciousIndentAfterControlStatement", + "cweIds": [ + 483 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AssignmentToSuperclassField", + "shortDescription": { + "text": "Constructor assigns value to field defined in superclass" + }, + "fullDescription": { + "text": "Reports assignment to, or modification of fields that are declared in a superclass from within a subclass constructor. It is considered preferable to initialize the fields of a superclass in its own constructor and delegate to that constructor in a subclass. This will also allow declaring a field 'final' if it isn't changed after the construction. Example: 'class Super {\n int x;\n }\n class Sub extends Super {\n Sub(int _x) {\n // Warning: x is declared in a superclass\n x = _x;\n }\n }' To avoid the problem, declare a superclass constructor: 'class Super {\n final int x;\n\n Super(int _x) {\n x = _x;\n }\n }\n class Sub extends Super {\n Sub(int _x) {\n super(_x);\n }\n }'", + "markdown": "Reports assignment to, or modification of fields that are declared in a superclass from within a subclass constructor.\n\nIt is considered preferable to initialize the fields of a superclass in its own constructor and\ndelegate to that constructor in a subclass. This will also allow declaring a field `final`\nif it isn't changed after the construction.\n\n**Example:**\n\n\n class Super {\n int x;\n }\n class Sub extends Super {\n Sub(int _x) {\n // Warning: x is declared in a superclass\n x = _x;\n }\n }\n\nTo avoid the problem, declare a superclass constructor:\n\n\n class Super {\n final int x;\n\n Super(int _x) {\n x = _x;\n }\n }\n class Sub extends Super {\n Sub(int _x) {\n super(_x);\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AssignmentToSuperclassField", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Assignment issues", + "index": 34, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FillPermitsList", + "shortDescription": { + "text": "Same file subclasses are missing from permits clause of a sealed class" + }, + "fullDescription": { + "text": "Reports sealed classes whose permits clauses do not contain some of the subclasses from the same file. Example: 'sealed class A {}\n final class B extends A {}' After the quick-fix is applied: 'sealed class A permits B {}\n final class B extends A {}' New in 2020.3", + "markdown": "Reports sealed classes whose permits clauses do not contain some of the subclasses from the same file.\n\nExample:\n\n\n sealed class A {}\n final class B extends A {}\n\nAfter the quick-fix is applied:\n\n\n sealed class A permits B {}\n final class B extends A {}\n\nNew in 2020.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "FillPermitsList", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Junit4Converter", + "shortDescription": { + "text": "JUnit 3 test can be JUnit 4" + }, + "fullDescription": { + "text": "Reports JUnit 3 test classes that can be converted to JUnit 4 test classes. Example: 'public class MainTestCase extends junit.framework.TestCase {\n public void test() {\n Assert.assertTrue(true);\n }\n }' After the quick-fix is applied: 'public class MainTestCase {\n @org.junit.Test\n public void test() {\n Assert.assertTrue(true);\n }\n }' This inspection only reports if the language level of the project or module is 5 or higher, and JUnit 4 is available on the classpath.", + "markdown": "Reports JUnit 3 test classes that can be converted to JUnit 4 test classes.\n\n**Example:**\n\n\n public class MainTestCase extends junit.framework.TestCase {\n public void test() {\n Assert.assertTrue(true);\n }\n }\n\nAfter the quick-fix is applied:\n\n\n public class MainTestCase {\n @org.junit.Test\n public void test() {\n Assert.assertTrue(true);\n }\n }\n\nThis inspection only reports if the language level of the project or module is 5 or higher, and JUnit 4 is available on the classpath." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "Junit4Converter", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages/Test frameworks", + "index": 94, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspiciousDateFormat", + "shortDescription": { + "text": "Suspicious date format pattern" + }, + "fullDescription": { + "text": "Reports date format patterns that are likely used by mistake. The following patterns are reported: Uppercase \"Y\", unless \"w\" appears nearby. It stands for \"Week year\" that is almost always the same as normal \"Year\" (lowercase \"y\" pattern), but may point to the next year at the end of December. Uppercase \"M\" (month) close to \"H\", \"K\", \"h\", or \"k\" (hour). It's likely that a lowercase \"m\" (minute) was intended. Lowercase \"m\" (minute) close to \"y\" (year) or \"d\" (day in month). It's likely that an uppercase \"M\" (month) was intended. Uppercase \"D\" (day in year) close to \"M\", or \"L\" (month). It's likely that a lowercase \"d\" (day in month) was intended. Uppercase \"S\" (milliseconds) close to \"m\" (minutes). It's likely that a lowercase \"s\" (seconds) was intended. Examples: 'new SimpleDateFormat(\"YYYY-MM-dd\")': likely '\"yyyy-MM-dd\"' was intended. 'new SimpleDateFormat(\"yyyy-MM-DD\")': likely '\"yyyy-MM-dd\"' was intended. 'new SimpleDateFormat(\"HH:MM\")': likely '\"HH:mm\"' was intended. New in 2020.1", + "markdown": "Reports date format patterns that are likely used by mistake.\n\nThe following patterns are reported:\n\n* Uppercase \"Y\", unless \"w\" appears nearby. It stands for \"Week year\" that is almost always the same as normal \"Year\" (lowercase \"y\" pattern), but may point to the next year at the end of December.\n* Uppercase \"M\" (month) close to \"H\", \"K\", \"h\", or \"k\" (hour). It's likely that a lowercase \"m\" (minute) was intended.\n* Lowercase \"m\" (minute) close to \"y\" (year) or \"d\" (day in month). It's likely that an uppercase \"M\" (month) was intended.\n* Uppercase \"D\" (day in year) close to \"M\", or \"L\" (month). It's likely that a lowercase \"d\" (day in month) was intended.\n* Uppercase \"S\" (milliseconds) close to \"m\" (minutes). It's likely that a lowercase \"s\" (seconds) was intended.\n\n\nExamples: \n\n`new SimpleDateFormat(\"YYYY-MM-dd\")`: likely `\"yyyy-MM-dd\"` was intended. \n\n`new SimpleDateFormat(\"yyyy-MM-DD\")`: likely `\"yyyy-MM-dd\"` was intended. \n\n`new SimpleDateFormat(\"HH:MM\")`: likely `\"HH:mm\"` was intended.\n\nNew in 2020.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SuspiciousDateFormat", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MagicConstant", + "shortDescription": { + "text": "Magic constant" + }, + "fullDescription": { + "text": "Reports expressions that can be replaced with \"magic\" constants. Example 1: '// Bare literal \"2\" is used, warning:\n Font font = new Font(\"Arial\", 2)' Example 2: '// Predefined constant is used, good:\n Font font = new Font(\"Arial\", Font.ITALIC)' When possible, the quick-fix inserts an appropriate predefined constant. The behavior of this inspection is controlled by 'org.intellij.lang.annotations.MagicConstant' annotation. Some standard Java library methods are pre-annotated, but you can use this annotation in your code as well.", + "markdown": "Reports expressions that can be replaced with \"magic\" constants.\n\nExample 1:\n\n\n // Bare literal \"2\" is used, warning:\n Font font = new Font(\"Arial\", 2)\n\nExample 2:\n\n\n // Predefined constant is used, good:\n Font font = new Font(\"Arial\", Font.ITALIC)\n\n\nWhen possible, the quick-fix inserts an appropriate predefined constant.\n\n\nThe behavior of this inspection is controlled by `org.intellij.lang.annotations.MagicConstant` annotation.\nSome standard Java library methods are pre-annotated, but you can use this annotation in your code as well." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "MagicConstant", + "cweIds": [ + 489 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassNewInstance", + "shortDescription": { + "text": "Unsafe call to 'Class.newInstance()'" + }, + "fullDescription": { + "text": "Reports calls to 'java.lang.Class.newInstance()'. This method propagates exceptions thrown by the no-arguments constructor, including checked exceptions. Usages of this method effectively bypass the compile-time exception checking that would otherwise be performed by the compiler. A quick-fix is suggested to replace the call with a call to the 'java.lang.reflect.Constructor.newInstance()' method, which avoids this problem by wrapping any exception thrown by the constructor in a (checked) 'java.lang.reflect.InvocationTargetException'. Example: 'clazz.newInstance()' After the quick-fix is applied: 'clazz.getConstructor().newInstance();'", + "markdown": "Reports calls to `java.lang.Class.newInstance()`.\n\n\nThis method propagates exceptions thrown by\nthe no-arguments constructor, including checked exceptions. Usages of this method\neffectively bypass the compile-time exception checking that would\notherwise be performed by the compiler.\n\n\nA quick-fix is suggested to replace the call with a call to the\n`java.lang.reflect.Constructor.newInstance()` method, which\navoids this problem by wrapping any exception thrown by the constructor in a\n(checked) `java.lang.reflect.InvocationTargetException`.\n\n**Example:**\n\n\n clazz.newInstance()\n\nAfter the quick-fix is applied:\n\n\n clazz.getConstructor().newInstance();\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassNewInstance", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LimitedScopeInnerClass", + "shortDescription": { + "text": "Local class" + }, + "fullDescription": { + "text": "Reports local classes. A local class is a named nested class declared inside a code block. Local classes are uncommon and may therefore be confusing. In addition, some code standards discourage the use of local classes. Example: 'void test() {\n class Local { // local class\n }\n new Local();\n }'", + "markdown": "Reports local classes.\n\nA local class is a named nested class declared inside a code block.\nLocal classes are uncommon and may therefore be confusing.\nIn addition, some code standards discourage the use of local classes.\n\n**Example:**\n\n\n void test() {\n class Local { // local class\n }\n new Local();\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LimitedScopeInnerClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MultiplyOrDivideByPowerOfTwo", + "shortDescription": { + "text": "Multiplication or division by power of two" + }, + "fullDescription": { + "text": "Reports multiplication of an integer value by a constant integer that can be represented as a power of two. Such expressions can be replaced with right or left shift operations for a possible performance improvement. Note that this inspection is not relevant for modern JVMs (e. g., HotSpot or OpenJ9) because their JIT compilers will perform this optimization. It might only be useful in some embedded systems where no JIT compilation is performed. Example: 'int y = x * 4;' A quick-fix is suggested to replace the multiplication or division operation with the shift operation: 'int y = x << 2;' Use the option to make the inspection also report division by a power of two. Note that replacing a power of two division with a shift does not work for negative numbers.", + "markdown": "Reports multiplication of an integer value by a constant integer that can be represented as a power of two. Such expressions can be replaced with right or left shift operations for a possible performance improvement.\n\n\nNote that this inspection is not relevant for modern JVMs (e. g.,\nHotSpot or OpenJ9) because their JIT compilers will perform this optimization.\nIt might only be useful in some embedded systems where no JIT compilation is performed.\n\n**Example:**\n\n\n int y = x * 4;\n\nA quick-fix is suggested to replace the multiplication or division operation with the shift operation:\n\n\n int y = x << 2;\n\n\nUse the option to make the inspection also report division by a power of two.\nNote that replacing a power of two division with a shift does not work for negative numbers." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MultiplyOrDivideByPowerOfTwo", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance/Embedded", + "index": 19, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MethodCallInLoopCondition", + "shortDescription": { + "text": "Method call in loop condition" + }, + "fullDescription": { + "text": "Reports method calls in the condition part of a loop statement. In highly resource constrained environments, such calls may have adverse performance implications. Applying the results of this inspection without consideration might have negative effects on code clarity and design. This inspection is intended for Java ME and other highly resource constrained environments. Example: 'String s = \"example\";\n for (int i = 0; i < s.length(); i++) {\n System.out.println(s.charAt(i));\n }' After the quick-fix is applied: 'String s = \"example\";\n int length = s.length();\n for (int i = 0; i < length; i++) {\n System.out.println(s.charAt(i));\n }' Use the option to ignore calls to common Java iteration methods like 'Iterator.hasNext()' and known methods with side-effects like 'Atomic*.compareAndSet'.", + "markdown": "Reports method calls in the condition part of a loop statement. In highly resource constrained environments, such calls may have adverse performance implications.\n\n\nApplying the results of this inspection without consideration might have negative effects on code clarity and design.\nThis inspection is intended for Java ME and other highly resource constrained environments.\n\n**Example:**\n\n\n String s = \"example\";\n for (int i = 0; i < s.length(); i++) {\n System.out.println(s.charAt(i));\n }\n\nAfter the quick-fix is applied:\n\n\n String s = \"example\";\n int length = s.length();\n for (int i = 0; i < length; i++) {\n System.out.println(s.charAt(i));\n }\n\n\nUse the option to ignore calls to common Java iteration methods like `Iterator.hasNext()`\nand known methods with side-effects like `Atomic*.compareAndSet`." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MethodCallInLoopCondition", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance/Embedded", + "index": 19, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NumericToString", + "shortDescription": { + "text": "Call to 'Number.toString()'" + }, + "fullDescription": { + "text": "Reports 'toString()' calls on objects of a class extending 'Number'. Such calls are usually incorrect in an internationalized environment and some locale specific formatting should be used instead. Example: 'void print(Double d) {\n System.out.println(d.toString());\n }' A possible way to fix this problem could be: 'void print(Double d) {\n System.out.printf(\"%f%n\", d);\n }' This formats the number using the default locale which is set during the startup of the JVM and is based on the host environment.", + "markdown": "Reports `toString()` calls on objects of a class extending `Number`. Such calls are usually incorrect in an internationalized environment and some locale specific formatting should be used instead.\n\n**Example:**\n\n\n void print(Double d) {\n System.out.println(d.toString());\n }\n\nA possible way to fix this problem could be:\n\n\n void print(Double d) {\n System.out.printf(\"%f%n\", d);\n }\n\nThis formats the number using the default locale which is set during the startup of the JVM and is based on the host environment." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToNumericToString", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Internationalization", + "index": 9, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MethodCount", + "shortDescription": { + "text": "Class with too many methods" + }, + "fullDescription": { + "text": "Reports classes whose number of methods exceeds the specified maximum. Classes with too many methods are often trying to 'do too much'. Consider splitting such a class into multiple smaller classes. Configure the inspection: Use the Method count limit field to specify the maximum allowed number of methods in a class. Use the Ignore simple getter and setter methods option to ignore simple getters and setters in method count. Use the Ignore methods overriding/implementing a super method to ignore methods that override or implement a method from a superclass.", + "markdown": "Reports classes whose number of methods exceeds the specified maximum.\n\nClasses with too many methods are often trying to 'do too much'. Consider splitting such a class into multiple smaller classes.\n\nConfigure the inspection:\n\n* Use the **Method count limit** field to specify the maximum allowed number of methods in a class.\n* Use the **Ignore simple getter and setter methods** option to ignore simple getters and setters in method count.\n* Use the **Ignore methods overriding/implementing a super method** to ignore methods that override or implement a method from a superclass." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassWithTooManyMethods", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class metrics", + "index": 87, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ForLoopReplaceableByWhile", + "shortDescription": { + "text": "'for' loop may be replaced by 'while' loop" + }, + "fullDescription": { + "text": "Reports 'for' loops that contain neither initialization nor update components, and suggests converting them to 'while' loops. This makes the code easier to read. Example: 'for(; exitCondition(); ) {\n process();\n }' After the quick-fix is applied: 'while(exitCondition()) {\n process();\n }' The quick-fix is also available for other 'for' loops, so you can replace any 'for' loop with a 'while' loop. Use the Ignore 'infinite' for loops without conditions option if you want to ignore 'for' loops with trivial or non-existent conditions.", + "markdown": "Reports `for` loops that contain neither initialization nor update components, and suggests converting them to `while` loops. This makes the code easier to read.\n\nExample:\n\n\n for(; exitCondition(); ) {\n process();\n }\n\nAfter the quick-fix is applied:\n\n\n while(exitCondition()) {\n process();\n }\n\nThe quick-fix is also available for other `for` loops, so you can replace any `for` loop with a\n`while` loop.\n\nUse the **Ignore 'infinite' for loops without conditions** option if you want to ignore `for`\nloops with trivial or non-existent conditions." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ForLoopReplaceableByWhile", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryDefault", + "shortDescription": { + "text": "Unnecessary 'default' for enum 'switch' statement" + }, + "fullDescription": { + "text": "Reports enum 'switch' statements or expression with 'default' branches which can never be taken, because all possible values are covered by a 'case' branch. Such elements are redundant, especially for 'switch' expressions, because they don't compile when all enum constants are not covered by a 'case' branch. The language level needs to be configured to 14 to report 'switch' expressions. The provided quick-fix removes 'default' branches. Example: 'enum E { A, B }\n int foo(E e) {\n return switch (e) {\n case A -> 1;\n case B -> 2;\n default -> 3;\n };\n }' After the quick-fix is applied: 'enum E { A, B }\n int foo(E e) {\n return switch (e) {\n case A -> 1;\n case B -> 2;\n };\n }' Use the Only report switch expressions option to report only redundant 'default' branches in switch expressions.", + "markdown": "Reports enum `switch` statements or expression with `default` branches which can never be taken, because all possible values are covered by a `case` branch.\n\nSuch elements are redundant, especially for `switch` expressions, because they don't compile when all\nenum constants are not covered by a `case` branch.\n\n\nThe language level needs to be configured to 14 to report `switch` expressions.\n\nThe provided quick-fix removes `default` branches.\n\nExample:\n\n\n enum E { A, B }\n int foo(E e) {\n return switch (e) {\n case A -> 1;\n case B -> 2;\n default -> 3;\n };\n }\n\nAfter the quick-fix is applied:\n\n\n enum E { A, B }\n int foo(E e) {\n return switch (e) {\n case A -> 1;\n case B -> 2;\n };\n }\n\nUse the **Only report switch expressions** option to report only redundant `default` branches in switch expressions." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryDefault", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StaticFieldReferenceOnSubclass", + "shortDescription": { + "text": "Static field referenced via subclass" + }, + "fullDescription": { + "text": "Reports accesses to static fields where the call is qualified by a subclass of the declaring class, rather than by the declaring class itself. Java allows such qualification, but such accesses may indicate a subtle confusion of inheritance and overriding. Example: 'class Parent {\n static int foo = 0;\n }\n\n class Child extends Parent { }\n\n void bar() {\n System.out.println(Child.foo);\n }' After the quick-fix is applied, the result looks like this: 'class Parent {\n static int foo = 0;\n }\n\n class Child extends Parent { }\n\n void bar() {\n System.out.println(Parent.foo);\n }'", + "markdown": "Reports accesses to static fields where the call is qualified by a subclass of the declaring class, rather than by the declaring class itself.\n\n\nJava allows such qualification, but such accesses may indicate a subtle confusion of inheritance and overriding.\n\n**Example:**\n\n\n class Parent {\n static int foo = 0;\n }\n\n class Child extends Parent { }\n\n void bar() {\n System.out.println(Child.foo);\n }\n\nAfter the quick-fix is applied, the result looks like this:\n\n\n class Parent {\n static int foo = 0;\n }\n\n class Child extends Parent { }\n\n void bar() {\n System.out.println(Parent.foo);\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StaticFieldReferencedViaSubclass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "VolatileArrayField", + "shortDescription": { + "text": "Volatile array field" + }, + "fullDescription": { + "text": "Reports array fields that are declared 'volatile'. Such declarations may be confusing because accessing the array itself follows the rules for 'volatile' fields, but accessing the array's contents does not. Example: 'class Data {\n private volatile int[] idx = new int[0];\n }' If such volatile access is needed for array contents, consider using 'java.util.concurrent.atomic' classes instead: 'class Data {\n private final AtomicIntegerArray idx = new AtomicIntegerArray(new int[0]);\n }'", + "markdown": "Reports array fields that are declared `volatile`. Such declarations may be confusing because accessing the array itself follows the rules for `volatile` fields, but accessing the array's contents does not.\n\n**Example:**\n\n\n class Data {\n private volatile int[] idx = new int[0];\n }\n\n\nIf such volatile access is needed for array contents, consider using\n`java.util.concurrent.atomic` classes instead:\n\n\n class Data {\n private final AtomicIntegerArray idx = new AtomicIntegerArray(new int[0]);\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "VolatileArrayField", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryInheritDoc", + "shortDescription": { + "text": "Unnecessary '{@inheritDoc}' Javadoc comment" + }, + "fullDescription": { + "text": "Reports Javadoc comments that contain only an '{@inheritDoc}' tag. Since Javadoc copies the super class' comment if no comment is present, a comment containing only '{@inheritDoc}' adds nothing. Also, it reports the '{@inheritDoc}' usages in invalid locations, for example, in fields. Suggests removing the unnecessary Javadoc comment. Example: 'class Example implements Comparable {\n /**\n * {@inheritDoc}\n */\n @Override\n public int compareTo(Example o) {\n return 0;\n }\n }' After the quick-fix is applied: 'class Example implements Comparable {\n @Override\n public int compareTo(Example o) {\n return 0;\n }\n }'", + "markdown": "Reports Javadoc comments that contain only an `{@inheritDoc}` tag. Since Javadoc copies the super class' comment if no comment is present, a comment containing only `{@inheritDoc}` adds nothing.\n\nAlso, it reports the `{@inheritDoc}` usages in invalid locations, for example, in fields.\n\nSuggests removing the unnecessary Javadoc comment.\n\n**Example:**\n\n\n class Example implements Comparable {\n /**\n * {@inheritDoc}\n */\n @Override\n public int compareTo(Example o) {\n return 0;\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Example implements Comparable {\n @Override\n public int compareTo(Example o) {\n return 0;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryInheritDoc", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Javadoc", + "index": 45, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IgnoredJUnitTest", + "shortDescription": { + "text": "JUnit test annotated with '@Ignore'/'@Disabled'" + }, + "fullDescription": { + "text": "Reports usages of JUnit 4's '@Ignore' or JUnit 5's '@Disabled' annotations. It is considered a code smell to have tests annotated with these annotations for a long time, especially when no reason is specified. Example: '@Ignore\n public class UrgentTest {\n\n @Test\n public void testIt() {\n Assert.assertEquals(\"expected\", \"actual\");\n }\n }' Configure the inspection: Use the Only report annotations without reason option to only report the cases when no reason is specified as the annotation's 'value' attribute.", + "markdown": "Reports usages of JUnit 4's `@Ignore` or JUnit 5's `@Disabled` annotations. It is considered a code smell to have tests annotated with these annotations for a long time, especially when no reason is specified.\n\n**Example:**\n\n\n @Ignore\n public class UrgentTest {\n\n @Test\n public void testIt() {\n Assert.assertEquals(\"expected\", \"actual\");\n }\n }\n\n\nConfigure the inspection:\n\n* Use the **Only report annotations without reason** option to only report the cases when no reason is specified as the annotation's `value` attribute." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "IgnoredJUnitTest", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages/Test frameworks", + "index": 94, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BlockingMethodInNonBlockingContext", + "shortDescription": { + "text": "Possibly blocking call in non-blocking context" + }, + "fullDescription": { + "text": "Reports thread-blocking method calls in code fragments where threads should not be blocked. Example (Project Reactor): 'Flux.just(\"1\").flatMap(f -> {\n Flux just = loadUsersFromDatabase();\n just.toIterable(); // Error: blocking operator call in non-blocking scope\n return just;\n }\n);' Consider running blocking code with a proper scheduler, for example 'Schedulers.boundedElastic()', or try to find an alternative non-blocking API. Example (Kotlin Coroutines): 'suspend fun exampleFun() {\n Thread.sleep(100); // Error: blocking method call inside suspend function\n}' Consider running blocking code with a special dispatcher, for example 'Dispatchers.IO', or try to find an alternative non-blocking API. Configure the inspection: In the Blocking Annotations list, specify annotations that mark thread-blocking methods. In the Non-Blocking Annotations list, specify annotations that mark non-blocking methods. Specified annotations can be used as External Annotations", + "markdown": "Reports thread-blocking method calls in code fragments where threads should not be blocked.\n\n**Example (Project Reactor):**\n\n\n Flux.just(\"1\").flatMap(f -> {\n Flux just = loadUsersFromDatabase();\n just.toIterable(); // Error: blocking operator call in non-blocking scope\n return just;\n }\n );\n\nConsider running blocking code [with a proper\nscheduler](https://projectreactor.io/docs/core/release/reference/#faq.wrap-blocking), for example `Schedulers.boundedElastic()`, or try to find an alternative non-blocking API.\n\n**Example (Kotlin Coroutines):**\n\n\n suspend fun exampleFun() {\n Thread.sleep(100); // Error: blocking method call inside suspend function\n }\n\nConsider running blocking code [with a special dispatcher](https://kotlinlang.org/docs/coroutine-context-and-dispatchers.html),\nfor example `Dispatchers.IO`, or try to find an alternative non-blocking API.\n\nConfigure the inspection:\n\n* In the **Blocking Annotations** list, specify annotations that mark thread-blocking methods.\n* In the **Non-Blocking Annotations** list, specify annotations that mark non-blocking methods.\n\nSpecified annotations can be used as [External Annotations](https://www.jetbrains.com/help/idea/external-annotations.html)" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "BlockingMethodInNonBlockingContext", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages", + "index": 2, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IgnoreResultOfCall", + "shortDescription": { + "text": "Result of method call ignored" + }, + "fullDescription": { + "text": "Reports method calls whose result is ignored. For many methods, ignoring the result is perfectly legitimate, but for some it is almost certainly an error. Examples of methods where ignoring the result is likely an error include 'java.io.inputStream.read()', which returns the number of bytes actually read, and any method on 'java.lang.String' or 'java.math.BigInteger'. These methods do not produce side-effects and thus pointless if their result is ignored. The calls to the following methods are inspected: Simple getters (which do nothing except return a field) Methods specified in the settings of this inspection Methods annotated with 'org.jetbrains.annotations.Contract(pure=true)' Methods annotated with .*.'CheckReturnValue' Methods in a class or package annotated with 'javax.annotation.CheckReturnValue' Optionally, all non-library methods Calls to methods annotated with Error Prone's or AssertJ's '@CanIgnoreReturnValue' annotation are not reported. Use the inspection settings to specify the classes to check. Methods are matched by name or name pattern using Java regular expression syntax. For classes, use fully-qualified names. Each entry applies to both the class and all its inheritors.", + "markdown": "Reports method calls whose result is ignored.\n\nFor many methods, ignoring the result is perfectly\nlegitimate, but for some it is almost certainly an error. Examples of methods where ignoring\nthe result is likely an error include `java.io.inputStream.read()`,\nwhich returns the number of bytes actually read, and any method on\n`java.lang.String` or `java.math.BigInteger`. These methods do not produce side-effects and thus pointless\nif their result is ignored.\n\nThe calls to the following methods are inspected:\n\n* Simple getters (which do nothing except return a field)\n* Methods specified in the settings of this inspection\n* Methods annotated with `org.jetbrains.annotations.Contract(pure=true)`\n* Methods annotated with .\\*.`CheckReturnValue`\n* Methods in a class or package annotated with `javax.annotation.CheckReturnValue`\n* Optionally, all non-library methods\n\nCalls to methods annotated with Error Prone's or AssertJ's `@CanIgnoreReturnValue` annotation are not reported.\n\n\nUse the inspection settings to specify the classes to check.\nMethods are matched by name or name pattern using Java regular expression syntax.\nFor classes, use fully-qualified names. Each entry applies to both the class and all its inheritors." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ResultOfMethodCallIgnored", + "cweIds": [ + 252, + 563 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ParametersPerConstructor", + "shortDescription": { + "text": "Constructor with too many parameters" + }, + "fullDescription": { + "text": "Reports constructors whose number of parameters exceeds the specified maximum. Such objects are hard to instantiate, especially if some parameters are optional. Constructors with too many parameters may indicate that refactoring is necessary. Consider applying the builder pattern, for example. Example: 'public BankAccount(long accountNumber,\n String owner,\n double balance,\n double interestRate) {\n // fields initialization\n }' Configure the inspection: Use the Parameter limit field to specify the maximum allowed number of parameters in a constructor. Use the Ignore constructors with visibility list to specify whether the inspection should ignore constructors with specific visibility.", + "markdown": "Reports constructors whose number of parameters exceeds the specified maximum. Such objects are hard to instantiate, especially if some parameters are optional. Constructors with too many parameters may indicate that refactoring is necessary. Consider applying the builder pattern, for example.\n\n**Example:**\n\n\n public BankAccount(long accountNumber,\n String owner,\n double balance,\n double interestRate) {\n // fields initialization\n }\n\nConfigure the inspection:\n\n* Use the **Parameter limit** field to specify the maximum allowed number of parameters in a constructor.\n* Use the **Ignore constructors with visibility** list to specify whether the inspection should ignore constructors with specific visibility." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ConstructorWithTooManyParameters", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Method metrics", + "index": 95, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonReproducibleMathCall", + "shortDescription": { + "text": "Non-reproducible call to 'Math'" + }, + "fullDescription": { + "text": "Reports calls to 'java.lang.Math' methods, which results are not guaranteed to be reproduced precisely. In environments where reproducibility of results is required, 'java.lang.StrictMath' should be used instead.", + "markdown": "Reports calls to `java.lang.Math` methods, which results are not guaranteed to be reproduced precisely.\n\nIn environments where reproducibility of results is required, `java.lang.StrictMath`\nshould be used instead." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonReproducibleMathCall", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AssertBetweenInconvertibleTypes", + "shortDescription": { + "text": "'assertEquals()' between objects of inconvertible types" + }, + "fullDescription": { + "text": "Reports calls to assertion methods where the \"expected\" and \"actual\" arguments are of incompatible types. Such calls often indicate that there is a bug in the test. This inspection checks the relevant JUnit, TestNG, and AssertJ methods. Examples: 'assertEquals(\"1\", 1);\n assertNotSame(new int[0], 0);\n\n // weak warning, may just test the equals() contract\n assertThat(foo).as(\"user type\").isNotEqualTo(bar);'", + "markdown": "Reports calls to assertion methods where the \"expected\" and \"actual\" arguments are of incompatible types.\n\nSuch calls often indicate that there is a bug in the test.\nThis inspection checks the relevant JUnit, TestNG, and AssertJ methods.\n\n**Examples:**\n\n\n assertEquals(\"1\", 1);\n assertNotSame(new int[0], 0);\n\n // weak warning, may just test the equals() contract\n assertThat(foo).as(\"user type\").isNotEqualTo(bar);\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "AssertBetweenInconvertibleTypes", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages/Test frameworks", + "index": 94, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SwitchExpressionCanBePushedDown", + "shortDescription": { + "text": "Common subexpression can be extracted from 'switch'" + }, + "fullDescription": { + "text": "Reports switch expressions and statements where every branch has a common subexpression, and the 'switch' can be moved inside. This action shortens the code. In many cases, it's reasonable to extract the resulting switch expression to a separate variable or method. Example: 'switch (value) {\n case 0 -> System.out.println(\"zero\");\n case 1 -> System.out.println(\"one\");\n case 2, 3, 4 -> System.out.println(\"few\");\n default -> System.out.println(\"many\");\n }' After the quick-fix is applied: 'System.out.println(switch (value) {\n case 0 -> \"zero\";\n case 1 -> \"one\";\n case 2, 3, 4 -> \"few\";\n default -> \"many\";\n });' This inspection is applicable only for enhanced switches with arrow syntax. New in 2022.3", + "markdown": "Reports switch expressions and statements where every branch has a common subexpression, and the `switch` can be moved inside. This action shortens the code. In many cases, it's reasonable to extract the resulting switch expression to a separate variable or method.\n\nExample:\n\n\n switch (value) {\n case 0 -> System.out.println(\"zero\");\n case 1 -> System.out.println(\"one\");\n case 2, 3, 4 -> System.out.println(\"few\");\n default -> System.out.println(\"many\");\n }\n\nAfter the quick-fix is applied:\n\n\n System.out.println(switch (value) {\n case 0 -> \"zero\";\n case 1 -> \"one\";\n case 2, 3, 4 -> \"few\";\n default -> \"many\";\n });\n\n\nThis inspection is applicable only for enhanced switches with arrow syntax.\n\nNew in 2022.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "SwitchExpressionCanBePushedDown", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonExtendableApiUsage", + "shortDescription": { + "text": "Class, interface, or method should not be extended" + }, + "fullDescription": { + "text": "Reports classes, interfaces and methods that extend, implement, or override API elements marked with '@ApiStatus.NonExtendable'. The '@ApiStatus.NonExtendable' annotation indicates that the class, interface, or method must not be extended, implemented, or overridden. Since casting such interfaces and classes to the internal library implementation is rather common, if a client provides a different implementation, you will get 'ClassCastException'. Adding new abstract methods to such classes and interfaces will break the compatibility with the client's implementations.", + "markdown": "Reports classes, interfaces and methods that extend, implement, or override API elements marked with `@ApiStatus.NonExtendable`.\n\n\nThe `@ApiStatus.NonExtendable` annotation indicates that the class, interface, or method **must not be extended,\nimplemented, or overridden** .\nSince casting such interfaces and classes to the internal library implementation is rather common,\nif a client provides a different implementation, you will get `ClassCastException`.\nAdding new abstract methods to such classes and interfaces will break the compatibility with the client's implementations." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "NonExtendableApiUsage", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages", + "index": 2, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnqualifiedFieldAccess", + "shortDescription": { + "text": "Instance field access not qualified with 'this'" + }, + "fullDescription": { + "text": "Reports field access operations that are not qualified with 'this' or some other qualifier. Some coding styles mandate that all field access operations are qualified to prevent confusion with local variable or parameter access. Example: 'class Foo {\n int foo;\n\n void bar() {\n foo += 1;\n }\n }' After the quick-fix is applied: 'class Foo {\n int foo;\n\n void bar() {\n this.foo += 1;\n }\n }'", + "markdown": "Reports field access operations that are not qualified with `this` or some other qualifier.\n\n\nSome coding styles mandate that all field access operations are qualified to prevent confusion with local\nvariable or parameter access.\n\n**Example:**\n\n\n class Foo {\n int foo;\n\n void bar() {\n foo += 1;\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo {\n int foo;\n\n void bar() {\n this.foo += 1;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnqualifiedFieldAccess", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MismatchedCollectionQueryUpdate", + "shortDescription": { + "text": "Mismatched query and update of collection" + }, + "fullDescription": { + "text": "Reports collections whose contents are either queried and not updated, or updated and not queried. Such inconsistent queries and updates are pointless and may indicate either dead code or a typo. Use the inspection settings to specify name patterns that correspond to update/query methods. Query methods that return an element are automatically detected, and only those that write data to an output parameter (for example, an 'OutputStream') need to be specified. Example: Suppose you have your custom 'FixedStack' class with method 'store()': 'public class FixedStack extends Collection {\n public T store(T t) {\n // implementation\n }\n }' You can add 'store' to the update methods table in order to report mismatched queries like: 'void test(int i) {\n FixedStack stack = new FixedStack<>();\n stack.store(i);\n }'", + "markdown": "Reports collections whose contents are either queried and not updated, or updated and not queried.\n\n\nSuch inconsistent queries and updates are pointless and may indicate\neither dead code or a typo.\n\n\nUse the inspection settings to specify name patterns that correspond to update/query methods.\nQuery methods that return an element are automatically detected, and only\nthose that write data to an output parameter (for example, an `OutputStream`) need to be specified.\n\n\n**Example:**\n\nSuppose you have your custom `FixedStack` class with method `store()`:\n\n\n public class FixedStack extends Collection {\n public T store(T t) {\n // implementation\n }\n }\n\nYou can add `store` to the update methods table in order to report mismatched queries like:\n\n\n void test(int i) {\n FixedStack stack = new FixedStack<>();\n stack.store(i);\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "MismatchedQueryAndUpdateOfCollection", + "cweIds": [ + 561, + 563 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CharacterComparison", + "shortDescription": { + "text": "Character comparison" + }, + "fullDescription": { + "text": "Reports ordinal comparisons of 'char' values. In an internationalized environment, such comparisons are rarely correct.", + "markdown": "Reports ordinal comparisons of `char` values. In an internationalized environment, such comparisons are rarely correct." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CharacterComparison", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Internationalization", + "index": 9, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConfusingElse", + "shortDescription": { + "text": "Redundant 'else'" + }, + "fullDescription": { + "text": "Reports redundant 'else' keywords in 'if'—'else' statements and statement chains. The 'else' keyword is redundant when all previous branches end with a 'return', 'throw', 'break', or 'continue' statement. In this case, the statements from the 'else' branch can be placed after the 'if' statement, and the 'else' keyword can be removed. Example: 'if (name == null) {\n throw new IllegalArgumentException();\n } else {\n System.out.println(name);\n }' After the quick-fix is applied: 'if (name == null) {\n throw new IllegalArgumentException();\n }\n System.out.println(name);' Disable the Report when there are no more statements after the 'if' statement option to ignore cases where the 'if'—'else' statement is the last statement in a code block.", + "markdown": "Reports redundant `else` keywords in `if`---`else` statements and statement chains.\n\n\nThe `else` keyword is redundant when all previous branches end with a\n`return`, `throw`, `break`, or `continue` statement. In this case,\nthe statements from the `else` branch can be placed after the `if` statement, and the\n`else` keyword can be removed.\n\n**Example:**\n\n\n if (name == null) {\n throw new IllegalArgumentException();\n } else {\n System.out.println(name);\n }\n\nAfter the quick-fix is applied:\n\n\n if (name == null) {\n throw new IllegalArgumentException();\n }\n System.out.println(name);\n\nDisable the **Report when there are no more statements after the 'if' statement** option to ignore cases where the `if`---`else` statement is the last statement in a code block." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ConfusingElseBranch", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InnerClassReferencedViaSubclass", + "shortDescription": { + "text": "Inner class referenced via subclass" + }, + "fullDescription": { + "text": "Reports accesses of inner and nested classes where the call is qualified by a subclass of the declaring class, rather than the declaring class itself. Java allows such qualification, but such accesses may indicate a subtle confusion of inheritance and overriding. Example: 'class Super {\n static class Inner {}\n }\n\n class Sub extends Super {\n void test() {\n Sub.Inner s = new Sub.Inner(); // 'Inner' class is declared in 'Super' class, but referenced via 'Sub' class\n }\n }' After the quick-fix is applied: 'class Super {\n static class Inner {}\n }\n\n class Sub extends Super {\n void test() {\n Super.Inner s = new Super.Inner();\n }\n }'", + "markdown": "Reports accesses of inner and nested classes where the call is qualified by a subclass of the declaring class, rather than the declaring class itself.\n\n\nJava allows such qualification, but such accesses may indicate a subtle confusion of inheritance and overriding.\n\n**Example:**\n\n\n class Super {\n static class Inner {}\n }\n\n class Sub extends Super {\n void test() {\n Sub.Inner s = new Sub.Inner(); // 'Inner' class is declared in 'Super' class, but referenced via 'Sub' class\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Super {\n static class Inner {}\n }\n\n class Sub extends Super {\n void test() {\n Super.Inner s = new Super.Inner();\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "InnerClassReferencedViaSubclass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ChannelResource", + "shortDescription": { + "text": "'Channel' opened but not safely closed" + }, + "fullDescription": { + "text": "Reports 'Channel' resources that are not safely closed, including any instances created by calling 'getChannel()' on a file or socket resource. By default, the inspection assumes that the resources can be closed by any method with 'close' or 'cleanup' in its name. Example: 'void send(Socket socket) throws IOException {\n SocketChannel channel = socket.getChannel(); //warning\n channel.write(ByteBuffer.wrap(\"message\".getBytes()));\n }' Use the following options to configure the inspection: Whether a 'Channel' resource is allowed to be opened inside a 'try' block. This style is less desirable because it is more verbose than opening a 'Channel' in front of a 'try' block. Whether the resource can be closed by any method call with the resource passed as argument.", + "markdown": "Reports `Channel` resources that are not safely closed, including any instances created by calling `getChannel()` on a file or socket resource.\n\n\nBy default, the inspection assumes that the resources can be closed by any method with\n'close' or 'cleanup' in its name.\n\n**Example:**\n\n\n void send(Socket socket) throws IOException {\n SocketChannel channel = socket.getChannel(); //warning\n channel.write(ByteBuffer.wrap(\"message\".getBytes()));\n }\n\n\nUse the following options to configure the inspection:\n\n* Whether a `Channel` resource is allowed to be opened inside a `try` block. This style is less desirable because it is more verbose than opening a `Channel` in front of a `try` block.\n* Whether the resource can be closed by any method call with the resource passed as argument." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ChannelOpenedButNotSafelyClosed", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Resource management", + "index": 47, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ScheduledThreadPoolExecutorWithZeroCoreThreads", + "shortDescription": { + "text": "'ScheduledThreadPoolExecutor' with zero core threads" + }, + "fullDescription": { + "text": "Reports any 'java.util.concurrent.ScheduledThreadPoolExecutor' instances in which 'corePoolSize' is set to zero via the 'setCorePoolSize' method or the object constructor. A 'ScheduledThreadPoolExecutor' with zero core threads will run nothing. Example: 'void foo(int corePoolSize) {\n if (corePoolSize != 0) return;\n ThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(corePoolSize); // warning\n executor.setCorePoolSize(corePoolSize); // warning\n }'", + "markdown": "Reports any `java.util.concurrent.ScheduledThreadPoolExecutor` instances in which `corePoolSize` is set to zero via the `setCorePoolSize` method or the object constructor.\n\n\nA `ScheduledThreadPoolExecutor` with zero core threads will run nothing.\n\n**Example:**\n\n\n void foo(int corePoolSize) {\n if (corePoolSize != 0) return;\n ThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(corePoolSize); // warning\n executor.setCorePoolSize(corePoolSize); // warning\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ScheduledThreadPoolExecutorWithZeroCoreThreads", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SynchronizeOnThis", + "shortDescription": { + "text": "Synchronization on 'this'" + }, + "fullDescription": { + "text": "Reports synchronization on 'this' or 'class' expressions. The reported constructs include 'synchronized' blocks and calls to 'wait()', 'notify()' or 'notifyAll()'. There are several reasons synchronization on 'this' or 'class' expressions may be a bad idea: it makes synchronization a part of the external interface of the class, which makes a future change to a different locking mechanism difficult, it becomes hard to track just who is locking on a given object, it makes a denial-of-service attack possible, either on purpose or it can happen easily by accident when subclassing. As an alternative, consider synchronizing on a 'private final' lock object, access to which can be completely controlled. Example: 'public void print() {\n synchronized(this) { // warning: Lock operations on 'this' may have unforeseen side-effects\n System.out.println(\"synchronized\");\n }\n }'", + "markdown": "Reports synchronization on `this` or `class` expressions. The reported constructs include `synchronized` blocks and calls to `wait()`, `notify()` or `notifyAll()`.\n\nThere are several reasons synchronization on `this` or `class` expressions may be a bad idea:\n\n1. it makes synchronization a part of the external interface of the class, which makes a future change to a different locking mechanism difficult,\n2. it becomes hard to track just who is locking on a given object,\n3. it makes a denial-of-service attack possible, either on purpose or it can happen easily by accident when subclassing.\n\nAs an alternative, consider synchronizing on a `private final` lock object, access to which can be completely controlled.\n\n**Example:**\n\n\n public void print() {\n synchronized(this) { // warning: Lock operations on 'this' may have unforeseen side-effects\n System.out.println(\"synchronized\");\n }\n }\n \n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SynchronizeOnThis", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnusedAssignment", + "shortDescription": { + "text": "Unused assignment" + }, + "fullDescription": { + "text": "Reports assignment values that are not used after assignment. If the assignment value is unused, it's better to remove the assignment to shorten the code and avoid redundant allocations. The following cases are reported: variables that are never read after assignment variables that are always overwritten with a new value before being read variable initializers that are redundant (for one of the two reasons above) Configure the inspection: Use the Report redundant initializers option to report redundant initializers: 'int getI() {\n int i = 0; // redundant initialization\n i = 2;\n return i;\n }' Use the Report prefix expressions that can be replaced with binary expressions option to report cases where an '++i' expression may be replaced with 'i + 1': 'int preInc(int value) {\n int res = value;\n return ++res;\n }' Use the Report postfix expressions where the changed value is not used option to report 'i++' cases where the value of 'i' is not used later: 'int postInc(int value) {\n int res = value;\n return res++;\n }' Use the Report pattern variables whose values are never used option to report cases where the value of a pattern variable is overwritten before it is read: 'if (object instanceof String s) {\n s = \"hello\";\n System.out.println(s);\n }' Use the Report iteration parameters whose values are never used option to report cases where the value of an iteration parameter of an enhanced 'for' statements is overwritten before it is read: 'for (String arg : args) {\n arg = \"test\";\n System.out.println(arg);\n }'", + "markdown": "Reports assignment values that are not used after assignment. If the assignment value is unused, it's better to remove the assignment to shorten the code and avoid redundant allocations.\n\nThe following cases are reported:\n\n* variables that are never read after assignment\n* variables that are always overwritten with a new value before being read\n* variable initializers that are redundant (for one of the two reasons above)\n\nConfigure the inspection:\n\n\nUse the **Report redundant initializers** option to report redundant initializers:\n\n\n int getI() {\n int i = 0; // redundant initialization\n i = 2;\n return i;\n }\n\n\nUse the **Report prefix expressions that can be replaced with binary expressions** option to report cases\nwhere an `++i` expression may be replaced with `i + 1`:\n\n\n int preInc(int value) {\n int res = value;\n return ++res;\n }\n\n\nUse the **Report postfix expressions where the changed value is not used** option to report `i++` cases\nwhere the value of `i` is not used later:\n\n\n int postInc(int value) {\n int res = value;\n return res++;\n }\n\n\nUse the **Report pattern variables whose values are never used** option to report cases where the value of a pattern variable\nis overwritten before it is read:\n\n\n if (object instanceof String s) {\n s = \"hello\";\n System.out.println(s);\n }\n\n\nUse the **Report iteration parameters whose values are never used** option to report cases where the value of an iteration parameter\nof an enhanced `for` statements is overwritten before it is read:\n\n\n for (String arg : args) {\n arg = \"test\";\n System.out.println(arg);\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "UnusedAssignment", + "cweIds": [ + 561, + 563 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "HashCodeUsesNonFinalVariable", + "shortDescription": { + "text": "Non-final field referenced in 'hashCode()'" + }, + "fullDescription": { + "text": "Reports implementations of 'hashCode()' that access non-'final' variables. Such access may result in 'hashCode()' returning different values at different points in the object's lifecycle, which may in turn cause problems when using the standard collections classes. Example: 'class Drink {\n String name;\n Drink(String name) { this.name = name; }\n @Override public int hashCode() {\n return Objects.hash(name); //warning\n }\n }\n ...\n Drink coffee = new Drink(\"Coffee\");\n priceMap.put(coffee, 10.0);\n coffee.name = \"Tea\";\n double coffeePrice = priceMap.get(coffee); //not found' A quick-fix is suggested to make the field final: 'class Drink {\n final String name;\n ...\n }'", + "markdown": "Reports implementations of `hashCode()` that access non-`final` variables.\n\n\nSuch access may result in `hashCode()`\nreturning different values at different points in the object's lifecycle, which may in turn cause problems when\nusing the standard collections classes.\n\n**Example:**\n\n\n class Drink {\n String name;\n Drink(String name) { this.name = name; }\n @Override public int hashCode() {\n return Objects.hash(name); //warning\n }\n }\n ...\n Drink coffee = new Drink(\"Coffee\");\n priceMap.put(coffee, 10.0);\n coffee.name = \"Tea\";\n double coffeePrice = priceMap.get(coffee); //not found\n\nA quick-fix is suggested to make the field final:\n\n\n class Drink {\n final String name;\n ...\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonFinalFieldReferencedInHashCode", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ProtectedField", + "shortDescription": { + "text": "Protected field" + }, + "fullDescription": { + "text": "Reports 'protected' fields. Constants (that is, variables marked 'static' or 'final') are not reported. Example: 'public class A {\n protected Object object; // warning\n protected final static int MODE = 0; // constant, no warning\n }'", + "markdown": "Reports `protected` fields.\n\nConstants (that is, variables marked `static` or `final`) are not reported.\n\n**Example:**\n\n\n public class A {\n protected Object object; // warning\n protected final static int MODE = 0; // constant, no warning\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ProtectedField", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Encapsulation", + "index": 60, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AssignmentUsedAsCondition", + "shortDescription": { + "text": "Assignment used as condition" + }, + "fullDescription": { + "text": "Reports assignments that are used as a condition of an 'if', 'while', 'for', or 'do' statement, or a conditional expression. Although occasionally intended, this usage is confusing and may indicate a typo, for example, '=' instead of '=='. The quick-fix replaces '=' with '=='. Example: 'void update(String str, boolean empty) {\n // Warning: 'empty' is reassigned,\n // not compared to str.isEmpty()\n if (empty = str.isEmpty()) {\n ...\n }\n }' After the quick-fix is applied: 'void update(String str, boolean empty) {\n if (empty == str.isEmpty()) {\n ...\n }\n }'", + "markdown": "Reports assignments that are used as a condition of an `if`, `while`, `for`, or `do` statement, or a conditional expression.\n\nAlthough occasionally intended, this usage is confusing and may indicate a typo, for example, `=` instead of `==`.\n\nThe quick-fix replaces `=` with `==`.\n\n**Example:**\n\n\n void update(String str, boolean empty) {\n // Warning: 'empty' is reassigned,\n // not compared to str.isEmpty()\n if (empty = str.isEmpty()) {\n ...\n }\n }\n\nAfter the quick-fix is applied:\n\n\n void update(String str, boolean empty) {\n if (empty == str.isEmpty()) {\n ...\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AssignmentUsedAsCondition", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Assignment issues", + "index": 34, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InstanceofThis", + "shortDescription": { + "text": "'instanceof' check for 'this'" + }, + "fullDescription": { + "text": "Reports usages of 'instanceof' or 'getClass() == SomeClass.class' in which a 'this' expression is checked. Such expressions indicate a failure of the object-oriented design, and should be replaced by polymorphic constructions. Example: 'class Super {\n void process() {\n if (this instanceof Sub) { // warning\n doSomething();\n } else {\n doSomethingElse();\n }\n }\n}\n \nclass Sub extends Super {}' To fix the problem, use an overriding method: 'class Super {\n void process() {\n doSomethingElse();\n }\n}\n \nclass Sub extends Super {\n @Override\n void process() {\n doSomething();\n }\n}'", + "markdown": "Reports usages of `instanceof` or `getClass() == SomeClass.class` in which a `this` expression is checked.\n\nSuch expressions indicate a failure of the object-oriented design, and should be replaced by\npolymorphic constructions.\n\nExample:\n\n\n class Super {\n void process() {\n if (this instanceof Sub) { // warning\n doSomething();\n } else {\n doSomethingElse();\n }\n }\n }\n \n class Sub extends Super {}\n\nTo fix the problem, use an overriding method:\n\n\n class Super {\n void process() {\n doSomethingElse();\n }\n }\n \n class Sub extends Super {\n @Override\n void process() {\n doSomething();\n }\n } \n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "InstanceofThis", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Abstraction issues", + "index": 78, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassMayBeInterface", + "shortDescription": { + "text": "Abstract 'class' may be 'interface'" + }, + "fullDescription": { + "text": "Reports 'abstract' classes that can be converted to interfaces. Using interfaces instead of classes is preferable as Java doesn't support multiple class inheritance, while a class can implement multiple interfaces. A class may be converted to an interface if it has no superclasses (other than Object), has only 'public static final' fields, 'public abstract' methods, and 'public' inner classes. Example: 'abstract class Example {\n public static final int MY_CONST = 42;\n public abstract void foo();\n}\n\nclass Inheritor extends Example {\n @Override\n public void foo() {\n System.out.println(MY_CONST);\n }\n}' After the quick-fix is applied: 'interface Example {\n int MY_CONST = 42;\n void foo();\n}\n\nclass Inheritor implements Example {\n @Override\n public void foo() {\n System.out.println(MY_CONST);\n }\n}' Configure the inspection: Use the Report classes containing non-abstract methods when using Java 8 option to report only the classes with 'static' methods and non-abstract methods that can be converted to 'default' methods (only applicable to language level of 8 or higher).", + "markdown": "Reports `abstract` classes that can be converted to interfaces.\n\nUsing interfaces instead of classes is preferable as Java doesn't support multiple class inheritance,\nwhile a class can implement multiple interfaces.\n\nA class may be converted to an interface if it has no superclasses (other\nthan Object), has only `public static final` fields,\n`public abstract` methods, and `public` inner classes.\n\n\nExample:\n\n\n abstract class Example {\n public static final int MY_CONST = 42;\n public abstract void foo();\n }\n\n class Inheritor extends Example {\n @Override\n public void foo() {\n System.out.println(MY_CONST);\n }\n }\n\nAfter the quick-fix is applied:\n\n\n interface Example {\n int MY_CONST = 42;\n void foo();\n }\n\n class Inheritor implements Example {\n @Override\n public void foo() {\n System.out.println(MY_CONST);\n }\n }\n\nConfigure the inspection:\n\n\nUse the **Report classes containing non-abstract methods when using Java 8** option to report only the classes with `static` methods and non-abstract methods that can be converted to\n`default` methods (only applicable to language level of 8 or higher)." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ClassMayBeInterface", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PackageInMultipleModules", + "shortDescription": { + "text": "Package with classes in multiple modules" + }, + "fullDescription": { + "text": "Reports non-empty packages that are present in several modules. When packages are contained in several modules, it is very easy to create a class with the same name in two modules. A module which depends on these modules will see a conflict if it tries to use such a class. The Java Platform Module System disallows packages contained in more than one module (also called split packages) Available only from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name and isn't reported in the editor.", + "markdown": "Reports non-empty packages that are present in several modules. When packages are contained in several modules, it is very easy to create a class with the same name in two modules. A module which depends on these modules will see a conflict if it tries to use such a class. The Java Platform Module System disallows packages contained in more than one module (also called *split packages* )\n\nAvailable only from **Code \\| Inspect Code** or\n**Code \\| Analyze Code \\| Run Inspection by Name** and isn't reported in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PackageInMultipleModules", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Packaging issues", + "index": 36, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryCallToStringValueOf", + "shortDescription": { + "text": "Unnecessary conversion to 'String'" + }, + "fullDescription": { + "text": "Reports unnecessary calls to static methods that convert their parameters to a string, e.g. 'String.valueOf()' or 'Integer.toString()'. Such calls are unnecessary when used in string concatenations. Example: 'System.out.println(\"Number: \" + Integer.toString(count));' After the quick-fix is applied: 'System.out.println(\"Number: \" + count);' Additionally such calls are unnecessary when used as arguments to library methods that do their own string conversion. Some examples of library methods that do their own string conversion are: Classes 'java.io.PrintWriter', 'java.io.PrintStream' 'print()', 'println()' Classes 'java.lang.StringBuilder', 'java.lang.StringBuffer' 'append()' Class 'org.slf4j.Logger' 'trace()', 'debug()', 'info()', 'warn()', 'error()' Use the Report calls that can be replaced with a concatenation with the empty string option to also report cases where concatenations with the empty string can be used instead of a call to 'String.valueOf()'.", + "markdown": "Reports unnecessary calls to static methods that convert their parameters to a string, e.g. `String.valueOf()` or `Integer.toString()`. Such calls are unnecessary when used in string concatenations.\n\nExample:\n\n\n System.out.println(\"Number: \" + Integer.toString(count));\n\nAfter the quick-fix is applied:\n\n\n System.out.println(\"Number: \" + count);\n\nAdditionally such calls are unnecessary when used as arguments to library methods that do their own string conversion. Some examples of library methods that do their own string conversion are:\n\n* Classes `java.io.PrintWriter`, `java.io.PrintStream`\n * `print()`, `println()`\n* Classes `java.lang.StringBuilder`, `java.lang.StringBuffer`\n * `append()`\n* Class `org.slf4j.Logger`\n * `trace()`, `debug()`, `info()`, `warn()`, `error()`\n\n\nUse the **Report calls that can be replaced with a concatenation with the empty string**\noption to also report cases where concatenations with the empty string can be used instead of a call to `String.valueOf()`." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryCallToStringValueOf", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FloatingPointEquality", + "shortDescription": { + "text": "Floating-point equality comparison" + }, + "fullDescription": { + "text": "Reports floating-point values that are being compared using the '==' or '!=' operator. Floating-point values are inherently inaccurate, and comparing them for exact equality is seldom the desired semantics. This inspection ignores comparisons with zero and infinity literals. Example: 'void m(double d1, double d2) {\n if (d1 == d2) {}\n }'", + "markdown": "Reports floating-point values that are being compared using the `==` or `!=` operator.\n\nFloating-point values are inherently inaccurate, and comparing them for exact equality is seldom the desired semantics.\n\nThis inspection ignores comparisons with zero and infinity literals.\n\n**Example:**\n\n\n void m(double d1, double d2) {\n if (d1 == d2) {}\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "FloatingPointEquality", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringReplaceableByStringBuffer", + "shortDescription": { + "text": "Non-constant 'String' can be replaced with 'StringBuilder'" + }, + "fullDescription": { + "text": "Reports variables declared as 'java.lang.String' that are repeatedly appended to. Such variables could be declared more efficiently as 'java.lang.StringBuffer' or 'java.lang.StringBuilder'. Example: 'String s = \"\";\n for (int i = 0; i < names.length; i++) {\n String name = names[i] + (i == names.length - 1 ? \"\" : \" \");\n s = s + name;\n }' Such a loop can be replaced with: 'StringBuilder s = new StringBuilder();\n for (int i = 0; i < names.length; i++) {\n String name = names[i] + (i == names.length - 1 ? \"\" : \" \");\n s.append(name);\n }' Or even with: 'String s = String.join(\" \", names);' Use the option to make this inspection only report when the variable is appended to in a loop.", + "markdown": "Reports variables declared as `java.lang.String` that are repeatedly appended to. Such variables could be declared more efficiently as `java.lang.StringBuffer` or `java.lang.StringBuilder`.\n\n**Example:**\n\n\n String s = \"\";\n for (int i = 0; i < names.length; i++) {\n String name = names[i] + (i == names.length - 1 ? \"\" : \" \");\n s = s + name;\n }\n\nSuch a loop can be replaced with:\n\n\n StringBuilder s = new StringBuilder();\n for (int i = 0; i < names.length; i++) {\n String name = names[i] + (i == names.length - 1 ? \"\" : \" \");\n s.append(name);\n }\n\nOr even with:\n\n\n String s = String.join(\" \", names);\n\n\nUse the option to make this inspection only report when the variable is appended to in a loop." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonConstantStringShouldBeStringBuffer", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SequencedCollectionMethodCanBeUsed", + "shortDescription": { + "text": "SequencedCollection method can be used" + }, + "fullDescription": { + "text": "Reports collection API method calls that can be simplified using Java 21 'SequencedCollection' methods. The following conversions are supported: 'list.add(0, element)' → 'list.addFirst(element);' 'list.get(0)' → 'list.getFirst();' 'list.get(list.size() - 1)' → 'list.getLast();' 'list.remove(0)' → 'list.removeFirst();' 'list.remove(list.size() - 1)' → 'list.removeLast();' 'collection.iterator().next()' → 'collection.getFirst();' New in 2023.3", + "markdown": "Reports collection API method calls that can be simplified using Java 21 `SequencedCollection` methods.\n\nThe following conversions are supported:\n\n* `list.add(0, element)` → `list.addFirst(element);`\n* `list.get(0)` → `list.getFirst();`\n* `list.get(list.size() - 1)` → `list.getLast();`\n* `list.remove(0)` → `list.removeFirst();`\n* `list.remove(list.size() - 1)` → `list.removeLast();`\n* `collection.iterator().next()` → `collection.getFirst();`\n\nNew in 2023.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SequencedCollectionMethodCanBeUsed", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 21", + "index": 103, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PublicField", + "shortDescription": { + "text": "'public' field" + }, + "fullDescription": { + "text": "Reports 'public' fields. Constants (fields marked with 'static' and 'final') are not reported. Example: 'class Main {\n public String name;\n }' After the quick-fix is applied: 'class Main {\n private String name;\n\n public String getName() {\n return name;\n }\n\n public void setName(String name) {\n this.name = name;\n }\n }' Configure the inspection: Use the Ignore If Annotated By list to specify annotations to ignore. The inspection will ignore fields with any of these annotations. Use the Ignore 'public final' fields of an enum option to ignore 'public final' fields of the 'enum' type.", + "markdown": "Reports `public` fields. Constants (fields marked with `static` and `final`) are not reported.\n\n**Example:**\n\n\n class Main {\n public String name;\n }\n\nAfter the quick-fix is applied:\n\n\n class Main {\n private String name;\n\n public String getName() {\n return name;\n }\n\n public void setName(String name) {\n this.name = name;\n }\n }\n\nConfigure the inspection:\n\n* Use the **Ignore If Annotated By** list to specify annotations to ignore. The inspection will ignore fields with any of these annotations.\n* Use the **Ignore 'public final' fields of an enum** option to ignore `public final` fields of the `enum` type." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "PublicField", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Encapsulation", + "index": 60, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringBufferToStringInConcatenation", + "shortDescription": { + "text": "'StringBuilder.toString()' in concatenation" + }, + "fullDescription": { + "text": "Reports 'StringBuffer.toString()' or 'StringBuilder.toString()' calls in string concatenations. Such calls are unnecessary when concatenating and can be removed, saving a method call and an object allocation, which may improve performance.", + "markdown": "Reports `StringBuffer.toString()` or `StringBuilder.toString()` calls in string concatenations. Such calls are unnecessary when concatenating and can be removed, saving a method call and an object allocation, which may improve performance." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StringBufferToStringInConcatenation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FinallyBlockCannotCompleteNormally", + "shortDescription": { + "text": "'finally' block which can not complete normally" + }, + "fullDescription": { + "text": "Reports 'return', 'throw', 'break', 'continue', and 'yield' statements that are used inside 'finally' blocks. These cause the 'finally' block to not complete normally but to complete abruptly. Any exceptions thrown from the 'try' and 'catch' blocks of the same 'try'-'catch' statement will be suppressed. Example: 'void x() {\n try {\n throw new RuntimeException();\n } finally {\n // if bar() returns true, the RuntimeException will be suppressed\n if (bar()) return;\n }\n }'", + "markdown": "Reports `return`, `throw`, `break`, `continue`, and `yield` statements that are used inside `finally` blocks. These cause the `finally` block to not complete normally but to complete abruptly. Any exceptions thrown from the `try` and `catch` blocks of the same `try`-`catch` statement will be suppressed.\n\n**Example:**\n\n\n void x() {\n try {\n throw new RuntimeException();\n } finally {\n // if bar() returns true, the RuntimeException will be suppressed\n if (bar()) return;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "finally", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConstantOnWrongSideOfComparison", + "shortDescription": { + "text": "Constant on wrong side of comparison" + }, + "fullDescription": { + "text": "Reports comparison operations where the constant value is on the wrong side. Some coding conventions specify that constants should be on a specific side of a comparison, either left or right. Example: 'boolean compare(int x) {\n return 1 > x; // Constant '1' on the left side of the comparison\n }' After the quick-fix is applied: 'boolean compare(int x) {\n return x < 1;\n }' Use the inspection settings to choose the side of constants in comparisons and whether to warn if 'null' literals are on the wrong side. New in 2019.2", + "markdown": "Reports comparison operations where the constant value is on the wrong side.\n\nSome coding conventions specify that constants should be on a specific side of a comparison, either left or right.\n\n**Example:**\n\n\n boolean compare(int x) {\n return 1 > x; // Constant '1' on the left side of the comparison\n }\n\nAfter the quick-fix is applied:\n\n\n boolean compare(int x) {\n return x < 1;\n }\n\n\nUse the inspection settings to choose the side of constants in comparisons\nand whether to warn if `null` literals are on the wrong side.\n\nNew in 2019.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ConstantOnWrongSideOfComparison", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SerialAnnotationUsedOnWrongMember", + "shortDescription": { + "text": "'@Serial' annotation used on wrong member" + }, + "fullDescription": { + "text": "Reports methods and fields in the 'Serializable' and 'Externalizable' classes that are not suitable to be annotated with the 'java.io.Serial' annotation. Examples: 'class Test implements Serializable {\n @Serial // The annotated field is not a part of serialization mechanism because it's not final\n private static long serialVersionUID = 7874493593505141603L;\n\n @Serial // The annotated method is not a part of the serialization mechanism because it's not private\n void writeObject(ObjectOutputStream out) throws IOException {\n }\n}' 'class Test implements Externalizable {\n @Serial // The annotated method is not a part of the serialization mechanism as it's inside Externalizable class\n private void writeObject(ObjectOutputStream out) throws IOException {\n }\n}' For information about all valid cases, refer the documentation for 'java.io.Serial'. This inspection only reports if the language level of the project or module is 14 or higher. New in 2020.3", + "markdown": "Reports methods and fields in the `Serializable` and `Externalizable` classes that are not suitable to be annotated with the `java.io.Serial` annotation.\n\n**Examples:**\n\n\n class Test implements Serializable {\n @Serial // The annotated field is not a part of serialization mechanism because it's not final\n private static long serialVersionUID = 7874493593505141603L;\n\n @Serial // The annotated method is not a part of the serialization mechanism because it's not private\n void writeObject(ObjectOutputStream out) throws IOException {\n }\n }\n\n\n class Test implements Externalizable {\n @Serial // The annotated method is not a part of the serialization mechanism as it's inside Externalizable class\n private void writeObject(ObjectOutputStream out) throws IOException {\n }\n }\n\nFor information about all valid cases, refer the documentation for `java.io.Serial`.\n\nThis inspection only reports if the language level of the project or module is 14 or higher.\n\nNew in 2020.3" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "serial", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ObjectToString", + "shortDescription": { + "text": "Call to default 'toString()'" + }, + "fullDescription": { + "text": "Reports calls to 'toString()' that use the default implementation from 'java.lang.Object'. The default implementation is rarely intended but may be used by accident. Calls to 'toString()' on objects with 'java.lang.Object', interface or abstract class type are ignored by this inspection. Example: 'class Bar {\n void foo1(Bar bar) {\n String s = bar.toString(); // warning\n /* ... */\n }\n\n void foo2(Object obj) {\n String s = obj.toString(); // no warning here\n /* ... */\n }\n }'", + "markdown": "Reports calls to `toString()` that use the default implementation from `java.lang.Object`.\n\nThe default implementation is rarely intended but may be used by accident.\n\n\nCalls to `toString()` on objects with `java.lang.Object`,\ninterface or abstract class type are ignored by this inspection.\n\n**Example:**\n\n\n class Bar {\n void foo1(Bar bar) {\n String s = bar.toString(); // warning\n /* ... */\n }\n\n void foo2(Object obj) {\n String s = obj.toString(); // no warning here\n /* ... */\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ObjectToString", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessarilyQualifiedStaticallyImportedElement", + "shortDescription": { + "text": "Unnecessarily qualified statically imported element" + }, + "fullDescription": { + "text": "Reports usage of statically imported members qualified with their containing class name. Such qualification is unnecessary and can be removed because statically imported members can be accessed directly by member name. Example: 'import static foo.Test.WIDTH;\n\n class Bar {\n void bar() {\n System.out.println(Test.WIDTH);\n }\n }' After the quick-fix is applied: 'import static foo.Test.WIDTH;\n\n class Bar {\n void bar() {\n System.out.println(WIDTH);\n }\n }'", + "markdown": "Reports usage of statically imported members qualified with their containing class name.\n\nSuch qualification is unnecessary and can be removed\nbecause statically imported members can be accessed directly by member name.\n\n**Example:**\n\n\n import static foo.Test.WIDTH;\n\n class Bar {\n void bar() {\n System.out.println(Test.WIDTH);\n }\n }\n\nAfter the quick-fix is applied:\n\n\n import static foo.Test.WIDTH;\n\n class Bar {\n void bar() {\n System.out.println(WIDTH);\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessarilyQualifiedStaticallyImportedElement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UseOfJDBCDriverClass", + "shortDescription": { + "text": "Use of concrete JDBC driver class" + }, + "fullDescription": { + "text": "Reports uses of specific JDBC driver classes. Use of such classes will bind your project to a specific database and driver, defeating the purpose of JDBC and resulting in loss of portability. Example: 'import java.sql.Driver;\n\n abstract class Sample implements Driver {\n public void foo() {\n Sample sample;\n }\n }'", + "markdown": "Reports uses of specific JDBC driver classes. Use of such classes will bind your project to a specific database and driver, defeating the purpose of JDBC and resulting in loss of portability.\n\n**Example:**\n\n\n import java.sql.Driver;\n\n abstract class Sample implements Driver {\n public void foo() {\n Sample sample;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UseOfJDBCDriverClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Portability", + "index": 7, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JDBCResource", + "shortDescription": { + "text": "JDBC resource opened but not safely closed" + }, + "fullDescription": { + "text": "Reports JDBC resources that are not safely closed. JDBC resources reported by this inspection include 'java.sql.Connection', 'java.sql.Statement', 'java.sql.PreparedStatement', 'java.sql.CallableStatement', and 'java.sql.ResultSet'. By default, the inspection assumes that the resources can be closed by any method with 'close' or 'cleanup' in its name. Example: 'ResultSet findAllElements(Connection connection) throws SQLException {\n PreparedStatement statement = connection.prepareStatement(\"SELECT * FROM TABLE\");//statement is not closed\n statement.execute();\n return statement.getResultSet();\n }' Use the following options to configure the inspection: Whether a JDBC resource is allowed to be opened inside a 'try' block. This style is less desirable because it is more verbose than opening a resource in front of a 'try' block. Whether the resource can be closed by any method call with the resource passed as argument.", + "markdown": "Reports JDBC resources that are not safely closed. JDBC resources reported by this inspection include `java.sql.Connection`, `java.sql.Statement`, `java.sql.PreparedStatement`, `java.sql.CallableStatement`, and `java.sql.ResultSet`.\n\n\nBy default, the inspection assumes that the resources can be closed by any method with\n'close' or 'cleanup' in its name.\n\n**Example:**\n\n\n ResultSet findAllElements(Connection connection) throws SQLException {\n PreparedStatement statement = connection.prepareStatement(\"SELECT * FROM TABLE\");//statement is not closed\n statement.execute();\n return statement.getResultSet();\n }\n\n\nUse the following options to configure the inspection:\n\n* Whether a JDBC resource is allowed to be opened inside a `try` block. This style is less desirable because it is more verbose than opening a resource in front of a `try` block.\n* Whether the resource can be closed by any method call with the resource passed as argument." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "JDBCResourceOpenedButNotSafelyClosed", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Resource management", + "index": 47, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ThrowsRuntimeException", + "shortDescription": { + "text": "Unchecked exception declared in 'throws' clause" + }, + "fullDescription": { + "text": "Reports declaration of an unchecked exception ('java.lang.RuntimeException' or one of its subclasses) in the 'throws' clause of a method. Declarations of unchecked exceptions are not required and may be deleted or moved to a Javadoc '@throws' tag. Example: 'public class InvalidDataException extends RuntimeException {}\n\n class TextEditor {\n void readSettings() throws InvalidDataException {} // warning: Unchecked exception 'InvalidDataException' declared in 'throws' clause\n }'", + "markdown": "Reports declaration of an unchecked exception (`java.lang.RuntimeException` or one of its subclasses) in the `throws` clause of a method.\n\nDeclarations of unchecked exceptions are not required and may be deleted or moved to a Javadoc `@throws` tag.\n\n**Example:**\n\n\n public class InvalidDataException extends RuntimeException {}\n\n class TextEditor {\n void readSettings() throws InvalidDataException {} // warning: Unchecked exception 'InvalidDataException' declared in 'throws' clause\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ThrowsRuntimeException", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NewObjectEquality", + "shortDescription": { + "text": "New object is compared using '=='" + }, + "fullDescription": { + "text": "Reports code that applies '==' or '!=' to a newly allocated object instead of calling 'equals()'. The references to newly allocated objects cannot point at existing objects, thus the comparison will always evaluate to 'false'. The inspection may also report newly created objects returned from simple methods. Example: 'void test(Object obj) {\n if (new Object() == obj) {...}\n }' After the quick-fix is applied: 'void test(Object obj) {\n if (new Object().equals(obj)) {...}\n }' New in 2018.3", + "markdown": "Reports code that applies `==` or `!=` to a newly allocated object instead of calling `equals()`.\n\n\nThe references to newly allocated objects cannot point at existing objects,\nthus the comparison will always evaluate to `false`. The inspection may also report newly\ncreated objects returned from simple methods.\n\n**Example:**\n\n\n void test(Object obj) {\n if (new Object() == obj) {...}\n }\n\nAfter the quick-fix is applied:\n\n\n void test(Object obj) {\n if (new Object().equals(obj)) {...}\n }\n\n\nNew in 2018.3" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "NewObjectEquality", + "cweIds": [ + 480 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JNDIResource", + "shortDescription": { + "text": "JNDI resource opened but not safely closed" + }, + "fullDescription": { + "text": "Reports JNDI resources that are not safely closed. JNDI resources reported by this inspection include 'javax.naming.InitialContext', and 'javax.naming.NamingEnumeration'. By default, the inspection assumes that the resources can be closed by any method with 'close' or 'cleanup' in its name. Example: 'Object findObject(Properties properties, String name) throws NamingException {\n Context context = new InitialContext(properties); //context is not closed\n return context.lookup(name);\n }' Use the following options to configure the inspection: Whether a JNDI Resource is allowed to be opened inside a 'try' block. This style is less desirable because it is more verbose than opening a resource in front of a 'try' block. Whether the resource can be closed by any method call with the resource passed as argument.", + "markdown": "Reports JNDI resources that are not safely closed. JNDI resources reported by this inspection include `javax.naming.InitialContext`, and `javax.naming.NamingEnumeration`.\n\n\nBy default, the inspection assumes that the resources can be closed by any method with\n'close' or 'cleanup' in its name.\n\n**Example:**\n\n\n Object findObject(Properties properties, String name) throws NamingException {\n Context context = new InitialContext(properties); //context is not closed\n return context.lookup(name);\n }\n\n\nUse the following options to configure the inspection:\n\n* Whether a JNDI Resource is allowed to be opened inside a `try` block. This style is less desirable because it is more verbose than opening a resource in front of a `try` block.\n* Whether the resource can be closed by any method call with the resource passed as argument." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "JNDIResourceOpenedButNotSafelyClosed", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Resource management", + "index": 47, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TryWithIdenticalCatches", + "shortDescription": { + "text": "Identical 'catch' branches in 'try' statement" + }, + "fullDescription": { + "text": "Reports identical 'catch' sections in a single 'try' statement. Collapsing such sections into one multi-catch block reduces code duplication and prevents the situations when one 'catch' section is updated, and another one is not. Example: 'try {\n doSmth();\n }\n catch (IOException e) {\n LOG.error(e);\n }\n catch (URISyntaxException e) {\n LOG.error(e);\n }' A quick-fix is available to make the code more compact: 'try {\n doSmth();\n }\n catch (IOException | URISyntaxException e) {\n LOG.error(e);\n }' This inspection only reports if the language level of the project or module is 7 or higher.", + "markdown": "Reports identical `catch` sections in a single `try` statement.\n\nCollapsing such sections into one *multi-catch* block reduces code duplication and prevents\nthe situations when one `catch` section is updated, and another one is not.\n\n**Example:**\n\n\n try {\n doSmth();\n }\n catch (IOException e) {\n LOG.error(e);\n }\n catch (URISyntaxException e) {\n LOG.error(e);\n }\n\nA quick-fix is available to make the code more compact:\n\n\n try {\n doSmth();\n }\n catch (IOException | URISyntaxException e) {\n LOG.error(e);\n }\n\nThis inspection only reports if the language level of the project or module is 7 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "TryWithIdenticalCatches", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 7", + "index": 112, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LambdaCanBeReplacedWithAnonymous", + "shortDescription": { + "text": "Lambda can be replaced with anonymous class" + }, + "fullDescription": { + "text": "Reports lambda expressions that can be replaced with anonymous classes. Expanding lambda expressions to anonymous classes may be useful if you need to implement other methods inside an anonymous class. Example: 's -> System.out.println(s)' After the quick-fix is applied: 'new Consumer() {\n @Override\n public void accept(String s) {\n System.out.println(s);\n }\n}' Lambda expression appeared in Java 8. This inspection can help to downgrade for backward compatibility with earlier Java versions.", + "markdown": "Reports lambda expressions that can be replaced with anonymous classes.\n\n\nExpanding lambda expressions to anonymous classes may be useful if you need to implement other\nmethods inside an anonymous class.\n\nExample:\n\n\n s -> System.out.println(s)\n\nAfter the quick-fix is applied:\n\n new Consumer() {\n @Override\n public void accept(String s) {\n System.out.println(s);\n }\n }\n\n\n*Lambda expression* appeared in Java 8.\nThis inspection can help to downgrade for backward compatibility with earlier Java versions." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "LambdaCanBeReplacedWithAnonymous", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantCompareCall", + "shortDescription": { + "text": "Redundant 'compare()' method call" + }, + "fullDescription": { + "text": "Reports comparisons in which the 'compare' method is superfluous. Example: 'boolean result = Integer.compare(a, b) == 0;' After the quick-fix is applied: 'boolean result = a == b;' New in 2018.2", + "markdown": "Reports comparisons in which the `compare` method is superfluous.\n\nExample:\n\n\n boolean result = Integer.compare(a, b) == 0;\n\nAfter the quick-fix is applied:\n\n\n boolean result = a == b;\n\nNew in 2018.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantCompareCall", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IfCanBeAssertion", + "shortDescription": { + "text": "Statement can be replaced with 'assert' or 'Objects.requireNonNull'" + }, + "fullDescription": { + "text": "Reports 'if' statements that throw only 'java.lang.Throwable' from a 'then' branch and do not have an 'else' branch. Such statements can be converted to more compact 'assert' statements. The inspection also reports Guava's 'Preconditions.checkNotNull()'. They can be replaced with a 'Objects.requireNonNull()' call for which a library may not be needed. Example: 'if (x == 2) throw new RuntimeException(\"fail\");\n if (y == null) throw new AssertionError();\n Preconditions.checkNotNull(z, \"z\");' After the quick-fix is applied: 'assert x != 2 : \"fail\";\n Objects.requireNonNull(y);\n Objects.requireNonNull(z, \"z\");' By default, this inspection provides a quick-fix in the editor without code highlighting.", + "markdown": "Reports `if` statements that throw only `java.lang.Throwable` from a `then` branch and do not have an `else` branch. Such statements can be converted to more compact `assert` statements.\n\n\nThe inspection also reports Guava's `Preconditions.checkNotNull()`.\nThey can be replaced with a `Objects.requireNonNull()` call for which a library may not be needed.\n\nExample:\n\n\n if (x == 2) throw new RuntimeException(\"fail\");\n if (y == null) throw new AssertionError();\n Preconditions.checkNotNull(z, \"z\");\n\nAfter the quick-fix is applied:\n\n\n assert x != 2 : \"fail\";\n Objects.requireNonNull(y);\n Objects.requireNonNull(z, \"z\");\n\nBy default, this inspection provides a quick-fix in the editor without code highlighting." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "IfCanBeAssertion", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TextBlockBackwardMigration", + "shortDescription": { + "text": "Text block can be replaced with regular string literal" + }, + "fullDescription": { + "text": "Reports text blocks that can be replaced with regular string literals. Example: 'Object obj = engine.eval(\"\"\"\n function hello() {\n print('\"Hello, world\"');\n }\n\n hello();\n \"\"\");' After the quick fix is applied: 'Object obj = engine.eval(\"function hello() {\\n\" +\n \" print('\\\"Hello, world\\\"');\\n\" +\n \"}\\n\" +\n \"\\n\" +\n \"hello();\\n\");' Text block appeared in Java 15. This inspection can help to downgrade for backward compatibility with earlier Java versions. New in 2019.3", + "markdown": "Reports text blocks that can be replaced with regular string literals.\n\n**Example:**\n\n\n Object obj = engine.eval(\"\"\"\n function hello() {\n print('\"Hello, world\"');\n }\n\n hello();\n \"\"\");\n\nAfter the quick fix is applied:\n\n\n Object obj = engine.eval(\"function hello() {\\n\" +\n \" print('\\\"Hello, world\\\"');\\n\" +\n \"}\\n\" +\n \"\\n\" +\n \"hello();\\n\");\n\n\n*Text block* appeared in Java 15.\nThis inspection can help to downgrade for backward compatibility with earlier Java versions.\n\nNew in 2019.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "TextBlockBackwardMigration", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 15", + "index": 99, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EmptyClass", + "shortDescription": { + "text": "Empty class" + }, + "fullDescription": { + "text": "Reports empty classes and empty Java files. A class is empty if it doesn't contain any fields, methods, constructors, or initializers. Empty classes are sometimes left over after significant changes or refactorings. Example: 'class Example {\n List getList() {\n return new ArrayList<>() {\n\n };\n }\n }' After the quick-fix is applied: 'class Example {\n List getList() {\n return new ArrayList<>();\n }\n }' Configure the inspection: Use the Ignore if annotated by option to specify special annotations. The inspection will ignore the classes marked with these annotations. Use the Ignore class if it is a parametrization of a super type option to ignore classes that parameterize a superclass. For example: 'class MyList extends ArrayList {}' Use the Ignore subclasses of java.lang.Throwable to ignore classes that extend 'java.lang.Throwable'. Use the Comments count as content option to ignore classes that contain comments.", + "markdown": "Reports empty classes and empty Java files.\n\nA class is empty if it doesn't contain any fields, methods, constructors, or initializers. Empty classes are sometimes left over\nafter significant changes or refactorings.\n\n**Example:**\n\n\n class Example {\n List getList() {\n return new ArrayList<>() {\n\n };\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Example {\n List getList() {\n return new ArrayList<>();\n }\n }\n\nConfigure the inspection:\n\n* Use the **Ignore if annotated by** option to specify special annotations. The inspection will ignore the classes marked with these annotations.\n*\n Use the **Ignore class if it is a parametrization of a super type** option to ignore classes that parameterize a superclass. For example:\n\n class MyList extends ArrayList {}\n\n* Use the **Ignore subclasses of java.lang.Throwable** to ignore classes that extend `java.lang.Throwable`.\n* Use the **Comments count as content** option to ignore classes that contain comments." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EmptyClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JavadocDeclaration", + "shortDescription": { + "text": "Javadoc declaration problems" + }, + "fullDescription": { + "text": "Reports Javadoc comments and tags with the following problems: invalid tag names incomplete tag descriptions duplicated tags missing Javadoc descriptions Example: '/**\n * Invalid tag name\n * @poram param description\n */\n public void sample(int param){\n }' Example: '/**\n * Pointing to itself {@link #sample(int)}\n */\n public void sample(int param){\n }' Quick-fix adds the unknown Javadoc tag to the list of user defined additional tags. Use textfield below to define additional Javadoc tags. Use first checkbox to ignore duplicated 'throws' tag. Use second checkbox to ignore problem with missing or incomplete first sentence in the description. Use third checkbox to ignore references pointing to itself.", + "markdown": "Reports Javadoc comments and tags with the following problems:\n\n* invalid tag names\n* incomplete tag descriptions\n* duplicated tags\n* missing Javadoc descriptions\n\nExample:\n\n\n /**\n * Invalid tag name\n * @poram param description\n */\n public void sample(int param){\n }\n\nExample:\n\n\n /**\n * Pointing to itself {@link #sample(int)}\n */\n public void sample(int param){\n }\n\nQuick-fix adds the unknown Javadoc tag to the list of user defined additional tags.\n\nUse textfield below to define additional Javadoc tags.\n\nUse first checkbox to ignore duplicated 'throws' tag.\n\nUse second checkbox to ignore problem with missing or incomplete first sentence in the description.\n\nUse third checkbox to ignore references pointing to itself." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "JavadocDeclaration", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Javadoc", + "index": 45, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConditionalCanBeOptional", + "shortDescription": { + "text": "Conditional can be replaced with Optional" + }, + "fullDescription": { + "text": "Reports null-check conditions and suggests replacing them with 'Optional' chains. Example: 'return str == null ? \"\" : str.trim();' After applying the quick-fix: 'return Optional.ofNullable(str).map(String::trim).orElse(\"\");' While the replacement is not always shorter, it could be helpful for further refactoring (for example, for changing the method return value to 'Optional'). Note that when a not-null branch of the condition returns null, the corresponding mapping step will produce an empty 'Optional' possibly changing the semantics. If it cannot be statically proven that semantics will be preserved, the quick-fix action name will contain the \"(may change semantics)\" notice, and the inspection highlighting will be turned off. This inspection only reports if the language level of the project or module is 8 or higher. New in 2018.1", + "markdown": "Reports null-check conditions and suggests replacing them with `Optional` chains.\n\nExample:\n\n\n return str == null ? \"\" : str.trim();\n\nAfter applying the quick-fix:\n\n\n return Optional.ofNullable(str).map(String::trim).orElse(\"\");\n\nWhile the replacement is not always shorter, it could be helpful for further refactoring\n(for example, for changing the method return value to `Optional`).\n\nNote that when a not-null branch of the condition returns null, the corresponding mapping step will\nproduce an empty `Optional` possibly changing the semantics. If it cannot be statically\nproven that semantics will be preserved, the quick-fix action name will contain the \"(may change semantics)\"\nnotice, and the inspection highlighting will be turned off.\n\nThis inspection only reports if the language level of the project or module is 8 or higher.\n\nNew in 2018.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ConditionalCanBeOptional", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AssignmentToMethodParameter", + "shortDescription": { + "text": "Assignment to method parameter" + }, + "fullDescription": { + "text": "Reports assignment to, or modification of method parameters. Although occasionally intended, this construct may be confusing and is therefore prohibited in some Java projects. The quick-fix adds a declaration of a new variable. Example: 'void printTrimmed(String s) {\n s = s.trim();\n System.out.println(s);\n }' After the quick-fix is applied: 'void printTrimmed(String s) {\n String trimmed = s.trim();\n System.out.println(trimmed);\n }' Use the Ignore if assignment is a transformation of the original parameter option to ignore assignments that modify the parameter value based on its previous value.", + "markdown": "Reports assignment to, or modification of method parameters.\n\nAlthough occasionally intended, this construct may be confusing\nand is therefore prohibited in some Java projects.\n\nThe quick-fix adds a declaration of a new variable.\n\n**Example:**\n\n\n void printTrimmed(String s) {\n s = s.trim();\n System.out.println(s);\n }\n\nAfter the quick-fix is applied:\n\n\n void printTrimmed(String s) {\n String trimmed = s.trim();\n System.out.println(trimmed);\n }\n\n\nUse the **Ignore if assignment is a transformation of the original parameter** option to ignore assignments that modify\nthe parameter value based on its previous value." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AssignmentToMethodParameter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Assignment issues", + "index": 34, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LocalVariableHidingMemberVariable", + "shortDescription": { + "text": "Local variable hides field" + }, + "fullDescription": { + "text": "Reports local variables named identically to a field of a surrounding class. As a result of such naming, you may accidentally use the variable where the identically named field is intended. A quick-fix is suggested to rename the variable. Example: 'public class Foo {\n public Object foo;\n\n void bar() {\n Object o = new Object() {\n void baz() {\n Object foo; // Local variable 'foo' hides field in class 'Foo'\n }\n };\n }\n }' You can configure the following options for this inspection: Ignore non-accessible fields - ignore local variables named identically to superclass fields that are not visible (for example, because they are private). Ignore local variables in a static context hiding non-static fields - for example when the local variable is inside a static method or inside a method which is inside a static inner class.", + "markdown": "Reports local variables named identically to a field of a surrounding class. As a result of such naming, you may accidentally use the variable where the identically named field is intended.\n\nA quick-fix is suggested to rename the variable.\n\n**Example:**\n\n\n public class Foo {\n public Object foo;\n\n void bar() {\n Object o = new Object() {\n void baz() {\n Object foo; // Local variable 'foo' hides field in class 'Foo'\n }\n };\n }\n }\n\n\nYou can configure the following options for this inspection:\n\n1. **Ignore non-accessible fields** - ignore local variables named identically to superclass fields that are not visible (for example, because they are private).\n2. **Ignore local variables in a static context hiding non-static fields** - for example when the local variable is inside a static method or inside a method which is inside a static inner class." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LocalVariableHidesMemberVariable", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Visibility", + "index": 57, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AssignmentToLambdaParameter", + "shortDescription": { + "text": "Assignment to lambda parameter" + }, + "fullDescription": { + "text": "Reports assignment to, or modification of lambda parameters. Although occasionally intended, this construct may be confusing and is often caused by a typo or use of a wrong variable. The quick-fix adds a declaration of a new variable. Example: 'list.forEach(s -> {\n s = s.trim();\n System.out.println(\"String: \" + s);\n });' After the quick-fix is applied: 'list.forEach(s -> {\n String trimmed = s.trim();\n System.out.println(\"String: \" + trimmed);\n });' Use the Ignore if assignment is a transformation of the original parameter option to ignore assignments that modify the parameter value based on its previous value.", + "markdown": "Reports assignment to, or modification of lambda parameters. Although occasionally intended, this construct may be confusing and is often caused by a typo or use of a wrong variable.\n\nThe quick-fix adds a declaration of a new variable.\n\n**Example:**\n\n\n list.forEach(s -> {\n s = s.trim();\n System.out.println(\"String: \" + s);\n });\n\nAfter the quick-fix is applied:\n\n\n list.forEach(s -> {\n String trimmed = s.trim();\n System.out.println(\"String: \" + trimmed);\n });\n\nUse the **Ignore if assignment is a transformation of the original parameter** option to ignore assignments that modify the parameter\nvalue based on its previous value." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AssignmentToLambdaParameter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Assignment issues", + "index": 34, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryTemporaryOnConversionToString", + "shortDescription": { + "text": "Unnecessary temporary object in conversion to 'String'" + }, + "fullDescription": { + "text": "Reports unnecessary creation of temporary objects when converting from a primitive type to 'String'. Example: 'String foo = new Integer(3).toString();' After the quick-fix is applied: 'String foo = Integer.toString(3);'", + "markdown": "Reports unnecessary creation of temporary objects when converting from a primitive type to `String`.\n\n**Example:**\n\n\n String foo = new Integer(3).toString();\n\nAfter the quick-fix is applied:\n\n\n String foo = Integer.toString(3);\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryTemporaryOnConversionToString", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SystemGetenv", + "shortDescription": { + "text": "Call to 'System.getenv()'" + }, + "fullDescription": { + "text": "Reports calls to 'System.getenv()'. Calls to 'System.getenv()' are inherently unportable.", + "markdown": "Reports calls to `System.getenv()`. Calls to `System.getenv()` are inherently unportable." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToSystemGetenv", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Portability", + "index": 7, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InterfaceMayBeAnnotatedFunctional", + "shortDescription": { + "text": "Interface may be annotated as '@FunctionalInterface'" + }, + "fullDescription": { + "text": "Reports interfaces that can be annotated with '@FunctionalInterface' (available since JDK 1.8). Annotating an interface with '@FunctionalInterface' indicates that the interface is functional and no more 'abstract' methods can be added to it. Example: 'interface FileProcessor {\n void execute(File file);\n }' After the quick-fix is applied: '@FunctionalInterface\n interface FileProcessor {\n void execute(File file);\n }' This inspection only reports if the language level of the project or module is 8 or higher.", + "markdown": "Reports interfaces that can be annotated with `@FunctionalInterface` (available since JDK 1.8).\n\nAnnotating an interface with `@FunctionalInterface` indicates that the interface\nis functional and no more `abstract` methods can be added to it.\n\n**Example:**\n\n\n interface FileProcessor {\n void execute(File file);\n }\n\nAfter the quick-fix is applied:\n\n\n @FunctionalInterface\n interface FileProcessor {\n void execute(File file);\n }\n\nThis inspection only reports if the language level of the project or module is 8 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "InterfaceMayBeAnnotatedFunctional", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BreakStatementWithLabel", + "shortDescription": { + "text": "'break' statement with label" + }, + "fullDescription": { + "text": "Reports 'break' statements with labels. Labeled 'break' statements complicate refactoring and can be confusing. Example: 'void handle(List strs) {\n outer:\n for (String s: strs) {\n for (char ch : s.toCharArray()) {\n if ('s' == ch) break outer;\n handleChar(ch);\n }\n }\n }'", + "markdown": "Reports `break` statements with labels.\n\nLabeled `break` statements complicate refactoring and can be confusing.\n\nExample:\n\n\n void handle(List strs) {\n outer:\n for (String s: strs) {\n for (char ch : s.toCharArray()) {\n if ('s' == ch) break outer;\n handleChar(ch);\n }\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "BreakStatementWithLabel", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspiciousLiteralUnderscore", + "shortDescription": { + "text": "Suspicious underscore in number literal" + }, + "fullDescription": { + "text": "Reports decimal number literals that use the underscore numeric separator with groups where the number of digits is not three. Such literals may contain a typo. This inspection will not warn on literals containing two consecutive underscores. It is also allowed to omit underscores in the fractional part of 'double' and 'float' literals. Example: 'int oneMillion = 1_000_0000;'", + "markdown": "Reports decimal number literals that use the underscore numeric separator with groups where the number of digits is not three. Such literals may contain a typo.\n\nThis inspection will not warn on literals containing two consecutive underscores.\nIt is also allowed to omit underscores in the fractional part of `double` and `float` literals.\n\n**Example:** `int oneMillion = 1_000_0000;`" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SuspiciousLiteralUnderscore", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringEquality", + "shortDescription": { + "text": "String comparison using '==', instead of 'equals()'" + }, + "fullDescription": { + "text": "Reports code that uses of == or != to compare strings. These operators determine referential equality instead of comparing content. In most cases, strings should be compared using 'equals()', which does a character-by-character comparison when the strings are different objects. Example: 'void foo(String s, String t) {\n final boolean b = t == s;\n }' If 't' is known to be non-null, then it's safe to apply the \"unsafe\" quick-fix and get the result similar to the following: 'void foo(String s, String t) {\n final boolean b = t.equals(s);\n }'", + "markdown": "Reports code that uses of **==** or **!=** to compare strings.\n\n\nThese operators determine referential equality instead of comparing content.\nIn most cases, strings should be compared using `equals()`,\nwhich does a character-by-character comparison when the strings are different objects.\n\n**Example:**\n\n\n void foo(String s, String t) {\n final boolean b = t == s;\n }\n\nIf `t` is known to be non-null, then it's safe to apply the \"unsafe\" quick-fix and get the result similar to the following:\n\n\n void foo(String s, String t) {\n final boolean b = t.equals(s);\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "StringEquality", + "cweIds": [ + 597 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StaticCallOnSubclass", + "shortDescription": { + "text": "Static method referenced via subclass" + }, + "fullDescription": { + "text": "Reports static method calls where the call is qualified by a subclass of the declaring class, rather than by the declaring class itself. Java allows such qualification for classes, but such calls may indicate a subtle confusion of inheritance and overriding. Example: 'class Parent {\n public static void print(String str) {}\n }\n class Child extends Parent {}\n\n Child.print(\"Hello, world!\");' After the quick-fix is applied: 'Parent.print(\"Hello, world!\");'", + "markdown": "Reports static method calls where the call is qualified by a subclass of the declaring class, rather than by the declaring class itself.\n\n\nJava allows such qualification for classes, but such calls\nmay indicate a subtle confusion of inheritance and overriding.\n\n**Example:**\n\n\n class Parent {\n public static void print(String str) {}\n }\n class Child extends Parent {}\n\n Child.print(\"Hello, world!\");\n\nAfter the quick-fix is applied:\n\n\n Parent.print(\"Hello, world!\");\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StaticMethodReferencedViaSubclass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConstantExpression", + "shortDescription": { + "text": "Constant expression can be evaluated" + }, + "fullDescription": { + "text": "Reports constant expressions, whose value can be evaluated statically, and suggests replacing them with their actual values. For example, you will be prompted to replace '2 + 2' with '4', or 'Math.sqrt(9.0)' with '3.0'. New in 2018.1", + "markdown": "Reports constant expressions, whose value can be evaluated statically, and suggests replacing them with their actual values. For example, you will be prompted to replace `2 + 2` with `4`, or `Math.sqrt(9.0)` with `3.0`.\n\nNew in 2018.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ConstantExpression", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReadResolveAndWriteReplaceProtected", + "shortDescription": { + "text": "'readResolve()' or 'writeReplace()' not declared 'protected'" + }, + "fullDescription": { + "text": "Reports classes that implement 'java.io.Serializable' where the 'readResolve()' or 'writeReplace()' methods are not declared 'protected'. Declaring 'readResolve()' and 'writeReplace()' methods 'private' can force subclasses to silently ignore them, while declaring them 'public' allows them to be invoked by untrusted code. If the containing class is declared 'final', these methods can be declared 'private'. Example: 'class ClassWithSerialization implements Serializable {\n public Object writeReplace() { // warning: 'writeReplace()' not declared protected\n ...\n }\n }'", + "markdown": "Reports classes that implement `java.io.Serializable` where the `readResolve()` or `writeReplace()` methods are not declared `protected`.\n\n\nDeclaring `readResolve()` and `writeReplace()` methods `private`\ncan force subclasses to silently ignore them, while declaring them\n`public` allows them to be invoked by untrusted code.\n\n\nIf the containing class is declared `final`, these methods can be declared `private`.\n\n**Example:**\n\n\n class ClassWithSerialization implements Serializable {\n public Object writeReplace() { // warning: 'writeReplace()' not declared protected\n ...\n }\n }\n \n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ReadResolveAndWriteReplaceProtected", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PackageDotHtmlMayBePackageInfo", + "shortDescription": { + "text": "'package.html' may be converted to 'package-info.java'" + }, + "fullDescription": { + "text": "Reports any 'package.html' files which are used for documenting packages. Since JDK 1.5, it is recommended that you use 'package-info.java' files instead, as such files can also contain package annotations. This way, package-info.java becomes a sole repository for package level annotations and documentation. Example: 'package.html' '\n \n Documentation example.\n \n' After the quick-fix is applied: 'package-info.java' '/**\n * Documentation example.\n */\npackage com.sample;'", + "markdown": "Reports any `package.html` files which are used for documenting packages.\n\nSince JDK 1.5, it is recommended that you use `package-info.java` files instead, as such\nfiles can also contain package annotations. This way, package-info.java becomes a\nsole repository for package level annotations and documentation.\n\nExample: `package.html`\n\n\n \n \n Documentation example.\n \n \n\nAfter the quick-fix is applied: `package-info.java`\n\n\n /**\n * Documentation example.\n */\n package com.sample;\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PackageDotHtmlMayBePackageInfo", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Javadoc", + "index": 45, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryLabelOnContinueStatement", + "shortDescription": { + "text": "Unnecessary label on 'continue' statement" + }, + "fullDescription": { + "text": "Reports 'continue' statements with unnecessary labels. Example: 'LABEL:\n while (a > b) {\n System.out.println(\"Hello\");\n //the code below is the last statement in a loop,\n //so unnecessary label and continue can be removed\n continue LABEL;\n }'", + "markdown": "Reports `continue` statements with unnecessary labels.\n\nExample:\n\n\n LABEL:\n while (a > b) {\n System.out.println(\"Hello\");\n //the code below is the last statement in a loop,\n //so unnecessary label and continue can be removed\n continue LABEL;\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryLabelOnContinueStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LongLiteralsEndingWithLowercaseL", + "shortDescription": { + "text": "'long' literal ending with 'l' instead of 'L'" + }, + "fullDescription": { + "text": "Reports 'long' literals ending with lowercase 'l'. These literals may be confusing, as the lowercase 'l' looks very similar to a literal '1' (one). Example: 'long nights = 100l;' After the quick-fix is applied: 'long nights = 100L;'", + "markdown": "Reports `long` literals ending with lowercase 'l'. These literals may be confusing, as the lowercase 'l' looks very similar to a literal '1' (one).\n\n**Example:**\n\n\n long nights = 100l;\n\nAfter the quick-fix is applied:\n\n\n long nights = 100L;\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "LongLiteralEndingWithLowercaseL", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantArrayCreation", + "shortDescription": { + "text": "Redundant array creation" + }, + "fullDescription": { + "text": "Reports arrays that are created specifically to be passed as a varargs parameter. Example: 'Arrays.asList(new String[]{\"Hello\", \"world\"})' The quick-fix replaces the array initializer with individual arguments: 'Arrays.asList(\"Hello\", \"world\")'", + "markdown": "Reports arrays that are created specifically to be passed as a varargs parameter.\n\nExample:\n\n`Arrays.asList(new String[]{\"Hello\", \"world\"})`\n\nThe quick-fix replaces the array initializer with individual arguments:\n\n`Arrays.asList(\"Hello\", \"world\")`" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantArrayCreation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ParameterNamingConvention", + "shortDescription": { + "text": "Method parameter naming convention" + }, + "fullDescription": { + "text": "Reports method parameters whose names are too short, too long, or do not follow the specified regular expression pattern. Example: 'void fooBar(int X)' should be reported if the inspection is enabled with the default settings in which a parameter name should start with a lowercase letter. Configure the inspection: Use the fields in the Options section to specify the minimum length, maximum length, and a regular expression expected for method parameter names. Specify 0 in order not to check the length of names. Regular expressions should be specified in the standard 'java.util.regex' format.", + "markdown": "Reports method parameters whose names are too short, too long, or do not follow the specified regular expression pattern.\n\n**Example:** `void fooBar(int X)`\nshould be reported if the inspection is enabled with the default settings in which a parameter name should start with a lowercase letter.\n\nConfigure the inspection:\n\n\nUse the fields in the **Options** section to specify the minimum length, maximum length, and a regular expression expected for\nmethod parameter names. Specify **0** in order not to check the length of names.\n\nRegular expressions should be specified in the standard `java.util.regex` format." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MethodParameterNamingConvention", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions", + "index": 51, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MethodCanBeVariableArityMethod", + "shortDescription": { + "text": "Method can have varargs parameter" + }, + "fullDescription": { + "text": "Reports methods that can be converted to variable arity methods. Example: 'void process(String name, Object[] objects);' After the quick-fix is applied: 'void process(String name, Object... objects);' This inspection only reports if the language level of the project or module is 5 or higher.", + "markdown": "Reports methods that can be converted to variable arity methods.\n\n**Example:**\n\n\n void process(String name, Object[] objects);\n\nAfter the quick-fix is applied:\n\n\n void process(String name, Object... objects);\n\n\nThis inspection only reports if the language level of the project or module is 5 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "MethodCanBeVariableArityMethod", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 5", + "index": 53, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonShortCircuitBoolean", + "shortDescription": { + "text": "Non-short-circuit boolean expression" + }, + "fullDescription": { + "text": "Reports usages of the non-short-circuit forms of boolean 'and' and 'or' ('&', '|', '&=' and '|='). Although the non-short-circuit versions are occasionally useful, in most cases the short-circuit forms ('&&' and '||') are intended and such unintentional usages may lead to subtle bugs. A quick-fix is suggested to use the short-circuit versions. Example: 'void foo(boolean x, boolean y, boolean z) {\n if (x | y) { x |= z; }\n }' After the quick-fix is applied: 'void foo(boolean x, boolean y) {\n if (x || y) { x = x || z; }\n }'", + "markdown": "Reports usages of the non-short-circuit forms of boolean 'and' and 'or' (`&`, `|`, `&=` and `|=`). Although the non-short-circuit versions are occasionally useful, in most cases the short-circuit forms (`&&` and `||`) are intended and such unintentional usages may lead to subtle bugs.\n\n\nA quick-fix is suggested to use the short-circuit versions.\n\n**Example:**\n\n\n void foo(boolean x, boolean y, boolean z) {\n if (x | y) { x |= z; }\n }\n\nAfter the quick-fix is applied:\n\n\n void foo(boolean x, boolean y) {\n if (x || y) { x = x || z; }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonShortCircuitBooleanExpression", + "cweIds": [ + 480, + 691 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AbstractClassNeverImplemented", + "shortDescription": { + "text": "Abstract class which has no concrete subclass" + }, + "fullDescription": { + "text": "Reports 'abstract' classes that have no concrete subclasses.", + "markdown": "Reports `abstract` classes that have no concrete subclasses." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AbstractClassNeverImplemented", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Inheritance issues", + "index": 25, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ThrowablePrintedToSystemOut", + "shortDescription": { + "text": "'Throwable' printed to 'System.out'" + }, + "fullDescription": { + "text": "Reports calls to 'System.out.println()' with an exception as an argument. Using print statements for logging exceptions hides the stack trace from you, which can complicate the investigation of the problem. It is recommended that you use logger instead. Calls to 'System.out.print()', 'System.err.println()', and 'System.err.print()' with an exception argument are also reported. It is better to use a logger to log exceptions instead. For example, instead of: 'try {\n foo();\n } catch (Exception e) {\n System.out.println(e);\n }' use the following code: 'try {\n foo();\n } catch (Exception e) {\n logger.warn(e); // logger call may be different\n }'", + "markdown": "Reports calls to `System.out.println()` with an exception as an argument.\n\nUsing print statements for logging exceptions hides the stack trace from you, which can complicate the investigation of the problem.\nIt is recommended that you use logger instead.\n\nCalls to `System.out.print()`, `System.err.println()`, and `System.err.print()` with an exception argument are also\nreported. It is better to use a logger to log exceptions instead.\n\nFor example, instead of:\n\n\n try {\n foo();\n } catch (Exception e) {\n System.out.println(e);\n }\n\nuse the following code:\n\n\n try {\n foo();\n } catch (Exception e) {\n logger.warn(e); // logger call may be different\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ThrowablePrintedToSystemOut", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code maturity", + "index": 50, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StreamToLoop", + "shortDescription": { + "text": "Stream API call chain can be replaced with loop" + }, + "fullDescription": { + "text": "Reports Stream API chains, 'Iterable.forEach()', and 'Map.forEach()' calls that can be automatically converted into classical loops. Example: 'String joinNonEmpty(List list) {\n return list.stream() // Stream can be converted to loop\n .filter(s -> !s.isEmpty())\n .map(String::trim)\n .collect(Collectors.joining(\", \"));\n }' After the quick-fix is applied: 'String joinNonEmpty(List list) {\n StringJoiner joiner = new StringJoiner(\", \");\n for (String s : list) {\n if (!s.isEmpty()) {\n String trim = s.trim();\n joiner.add(trim);\n }\n }\n return joiner.toString();\n }' Note that sometimes this inspection might cause slight semantic changes. Special care should be taken when it comes to short-circuiting, as it's not specified how many elements will be actually read when the stream short-circuits. Stream API appeared in Java 8. This inspection can help to downgrade for backward compatibility with earlier Java versions. Configure the inspection: Use the Iterate unknown Stream sources via Stream.iterator() option to suggest conversions for streams with unrecognized source. In this case, iterator will be created from the stream. For example, when checkbox is selected, the conversion will be suggested here: 'List handles = ProcessHandle.allProcesses().collect(Collectors.toList());' In this case, the result will be as follows: 'List handles = new ArrayList<>();\n for (Iterator it = ProcessHandle.allProcesses().iterator(); it.hasNext(); ) {\n ProcessHandle allProcess = it.next();\n handles.add(allProcess);\n }' New in 2017.1", + "markdown": "Reports Stream API chains, `Iterable.forEach()`, and `Map.forEach()` calls that can be automatically converted into classical loops.\n\n**Example:**\n\n\n String joinNonEmpty(List list) {\n return list.stream() // Stream can be converted to loop\n .filter(s -> !s.isEmpty())\n .map(String::trim)\n .collect(Collectors.joining(\", \"));\n }\n\nAfter the quick-fix is applied:\n\n\n String joinNonEmpty(List list) {\n StringJoiner joiner = new StringJoiner(\", \");\n for (String s : list) {\n if (!s.isEmpty()) {\n String trim = s.trim();\n joiner.add(trim);\n }\n }\n return joiner.toString();\n }\n\n\nNote that sometimes this inspection might cause slight semantic changes.\nSpecial care should be taken when it comes to short-circuiting, as it's not specified how many elements will be actually read when\nthe stream short-circuits.\n\n\n*Stream API* appeared in Java 8.\nThis inspection can help to downgrade for backward compatibility with earlier Java versions.\n\nConfigure the inspection:\n\nUse the **Iterate unknown Stream sources via Stream.iterator()** option to suggest conversions for streams with unrecognized source.\nIn this case, iterator will be created from the stream.\nFor example, when checkbox is selected, the conversion will be suggested here:\n\n\n List handles = ProcessHandle.allProcesses().collect(Collectors.toList());\n\nIn this case, the result will be as follows:\n\n\n List handles = new ArrayList<>();\n for (Iterator it = ProcessHandle.allProcesses().iterator(); it.hasNext(); ) {\n ProcessHandle allProcess = it.next();\n handles.add(allProcess);\n }\n\nNew in 2017.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "StreamToLoop", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantRecordConstructor", + "shortDescription": { + "text": "Redundant record constructor" + }, + "fullDescription": { + "text": "Reports redundant constructors declared inside Java records. Example 1: 'record Point(int x, int y) {\n public Point {} // could be removed\n }\n \n record Point(int x, int y) {\n public Point(int x, int y) { // could be removed\n this.x = x;\n this.y = y;\n }\n }' The quick-fix removes the redundant constructors. Example 2: '// could be converted to compact constructor\n record Range(int from, int to) {\n public Range(int from, int to) {\n if (from > to) throw new IllegalArgumentException();\n this.from = from;\n this.to = to;\n }\n }' The quick-fix converts this code into a compact constructor. This inspection only reports if the language level of the project or module is 16 or higher. New in 2020.1", + "markdown": "Reports redundant constructors declared inside Java records.\n\n**Example 1:**\n\n\n record Point(int x, int y) {\n public Point {} // could be removed\n }\n \n record Point(int x, int y) {\n public Point(int x, int y) { // could be removed\n this.x = x;\n this.y = y;\n }\n }\n\nThe quick-fix removes the redundant constructors.\n\n**Example 2:**\n\n\n // could be converted to compact constructor\n record Range(int from, int to) {\n public Range(int from, int to) {\n if (from > to) throw new IllegalArgumentException();\n this.from = from;\n this.to = to;\n }\n }\n\nThe quick-fix converts this code into a compact constructor.\n\nThis inspection only reports if the language level of the project or module is 16 or higher.\n\nNew in 2020.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantRecordConstructor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NotifyWithoutCorrespondingWait", + "shortDescription": { + "text": "'notify()' without corresponding 'wait()'" + }, + "fullDescription": { + "text": "Reports calls to 'Object.notify()' or 'Object.notifyAll()' for which no call to a corresponding 'Object.wait()' can be found. Only calls that target fields of the current class are reported by this inspection. Example: 'synchronized (synList) {\n synList.notify(); //synList.wait() is never called\n }'", + "markdown": "Reports calls to `Object.notify()` or `Object.notifyAll()` for which no call to a corresponding `Object.wait()` can be found.\n\nOnly calls that target fields of the current class are reported by this inspection.\n\n**Example:**\n\n\n synchronized (synList) {\n synList.notify(); //synList.wait() is never called\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NotifyWithoutCorrespondingWait", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AmbiguousMethodCall", + "shortDescription": { + "text": "Call to inherited method looks like call to local method" + }, + "fullDescription": { + "text": "Reports calls to a superclass method from an anonymous, inner or local class, if a method with the same signature exists in the code surrounding the class. In this case it may seem that a method from the surrounding code is called, when in fact it is a call to a method from the superclass. To clarify the intent of the code, it is recommended to add an explicit 'super' qualifier to the method call. Example: 'class Parent {\n void ambiguous(){}\n }\n\n class Example {\n void ambiguous(){}\n\n class Inner extends Parent {\n void example(){\n ambiguous(); //warning\n }\n }\n }' After the quick-fix is applied: 'class Parent {\n void ambiguous(){}\n }\n\n class Example {\n void ambiguous(){}\n\n class Inner extends Parent {\n void example(){\n super.ambiguous();\n }\n }\n }'", + "markdown": "Reports calls to a superclass method from an anonymous, inner or local class, if a method with the same signature exists in the code surrounding the class. In this case it may seem that a method from the surrounding code is called, when in fact it is a call to a method from the superclass.\n\n\nTo clarify the intent of the code, it is recommended to add an explicit\n`super` qualifier to the method call.\n\n**Example:**\n\n\n class Parent {\n void ambiguous(){}\n }\n\n class Example {\n void ambiguous(){}\n\n class Inner extends Parent {\n void example(){\n ambiguous(); //warning\n }\n }\n }\n \nAfter the quick-fix is applied:\n\n\n class Parent {\n void ambiguous(){}\n }\n\n class Example {\n void ambiguous(){}\n\n class Inner extends Parent {\n void example(){\n super.ambiguous();\n }\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AmbiguousMethodCall", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Visibility", + "index": 57, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassInitializerMayBeStatic", + "shortDescription": { + "text": "Class initializer may be 'static'" + }, + "fullDescription": { + "text": "Reports instance initializers which may be made 'static'. An instance initializer may be static if it does not reference any of its class' non-static members. Static initializers are executed once the class is resolved, while instance initializers are executed on each instantiation of the class. This inspection doesn't report instance empty initializers and initializers in anonymous classes. Example: 'class A {\n public static String CONSTANT;\n {\n CONSTANT = \"Hello\";\n }\n }' After the quick-fix is applied: 'class A {\n public static String CONSTANT;\n static {\n CONSTANT = \"Hello\"; //now initialized only once per class\n }\n }'", + "markdown": "Reports instance initializers which may be made `static`.\n\n\nAn instance initializer may be static if it does not reference any of its class' non-static members.\nStatic initializers are executed once the class is resolved,\nwhile instance initializers are executed on each instantiation of the class.\n\nThis inspection doesn't report instance empty initializers and initializers in anonymous classes.\n\n**Example:**\n\n\n class A {\n public static String CONSTANT;\n {\n CONSTANT = \"Hello\";\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class A {\n public static String CONSTANT;\n static {\n CONSTANT = \"Hello\"; //now initialized only once per class\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ClassInitializerMayBeStatic", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MagicCharacter", + "shortDescription": { + "text": "Magic character" + }, + "fullDescription": { + "text": "Reports character literals that are used without constant declaration. These characters might result in bad code readability. Also, there might be errors if a character is changed only in one location but not everywhere in code. Example: 'char c = 'c';'", + "markdown": "Reports character literals that are used without constant declaration. These characters might result in bad code readability. Also, there might be errors if a character is changed only in one location but not everywhere in code.\n\n**Example:**\n\n char c = 'c';\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MagicCharacter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Internationalization", + "index": 9, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SlowAbstractSetRemoveAll", + "shortDescription": { + "text": "Call to 'set.removeAll(list)' may work slowly" + }, + "fullDescription": { + "text": "Reports calls to 'java.util.Set.removeAll()' with a 'java.util.List' argument. Such a call can be slow when the size of the argument is greater than or equal to the size of the set, and the set is a subclass of 'java.util.AbstractSet'. In this case, 'List.contains()' is called for each element in the set, which will perform a linear search. Example: 'public void check(String... ss) {\n // possible O(n^2) complexity\n mySet.removeAll(List.of(ss));\n }' After the quick fix is applied: 'public void check(String... ss) {\n // O(n) complexity\n List.of(ss).forEach(mySet::remove);\n }' New in 2020.3", + "markdown": "Reports calls to `java.util.Set.removeAll()` with a `java.util.List` argument.\n\n\nSuch a call can be slow when the size of the argument is greater than or equal to the size of the set,\nand the set is a subclass of `java.util.AbstractSet`.\nIn this case, `List.contains()` is called for each element in the set, which will perform a linear search.\n\n**Example:**\n\n\n public void check(String... ss) {\n // possible O(n^2) complexity\n mySet.removeAll(List.of(ss));\n }\n\nAfter the quick fix is applied:\n\n\n public void check(String... ss) {\n // O(n) complexity\n List.of(ss).forEach(mySet::remove);\n }\n\nNew in 2020.3" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SlowAbstractSetRemoveAll", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ArrayEquality", + "shortDescription": { + "text": "Array comparison using '==', instead of 'Arrays.equals()'" + }, + "fullDescription": { + "text": "Reports operators '==' and '!=' used to test for array equality. In most cases, testing for the equality of array contents is intended, which can be done with the 'java.util.Arrays.equals()' method. A quick-fix is suggested to replace '==' with 'java.util.Arrays.equals()'. Example: 'void foo(Object[] x, Object[] y) {\n boolean comparison = x == y;\n }' After the quick-fix is applied: 'void foo(Object[] x, Object[] y) {\n boolean comparison = Arrays.equals(x, y);\n }'", + "markdown": "Reports operators `==` and `!=` used to test for array equality. In most cases, testing for the equality of array contents is intended, which can be done with the `java.util.Arrays.equals()` method.\n\n\nA quick-fix is suggested to replace `==` with `java.util.Arrays.equals()`.\n\n**Example:**\n\n\n void foo(Object[] x, Object[] y) {\n boolean comparison = x == y;\n }\n\nAfter the quick-fix is applied:\n\n\n void foo(Object[] x, Object[] y) {\n boolean comparison = Arrays.equals(x, y);\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ArrayEquality", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StaticInheritance", + "shortDescription": { + "text": "Static inheritance" + }, + "fullDescription": { + "text": "Reports interfaces that are implemented only to provide access to constants. This kind of inheritance is often confusing and may hide important dependency information.", + "markdown": "Reports interfaces that are implemented only to provide access to constants. This kind of inheritance is often confusing and may hide important dependency information." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StaticInheritance", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Inheritance issues", + "index": 25, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StaticCollection", + "shortDescription": { + "text": "Static collection" + }, + "fullDescription": { + "text": "Reports static fields of a 'Collection' type. While it's not necessarily a problem, static collections often cause memory leaks and are therefore prohibited by some coding standards. Example: 'public class Example {\n static List list = new ArrayList<>();\n\n }' Configure the inspection: Use the Ignore weak static collections or maps option to ignore the fields of the 'java.util.WeakHashMap' type.", + "markdown": "Reports static fields of a `Collection` type. While it's not necessarily a problem, static collections often cause memory leaks and are therefore prohibited by some coding standards.\n\n**Example:**\n\n\n public class Example {\n static List list = new ArrayList<>();\n\n }\n\n\nConfigure the inspection:\n\n* Use the **Ignore weak static collections or maps** option to ignore the fields of the `java.util.WeakHashMap` type." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StaticCollection", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Memory", + "index": 73, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantUnmodifiable", + "shortDescription": { + "text": "Redundant usage of unmodifiable collection wrappers" + }, + "fullDescription": { + "text": "Reports redundant calls to unmodifiable collection wrappers from the 'Collections' class. If the argument that is passed to an unmodifiable collection wrapper is already immutable, such a wrapping becomes redundant. Example: 'List x = Collections.unmodifiableList(Collections.singletonList(\"abc\"));' After the quick-fix is applied: 'List x = Collections.singletonList(\"abc\");' In order to detect the methods that return unmodifiable collections, the inspection uses the 'org.jetbrains.annotations.Unmodifiable' and 'org.jetbrains.annotations.UnmodifiableView' annotations. Use them to extend the inspection to your own unmodifiable collection wrappers. New in 2020.3", + "markdown": "Reports redundant calls to unmodifiable collection wrappers from the `Collections` class.\n\nIf the argument that is passed to an unmodifiable\ncollection wrapper is already immutable, such a wrapping becomes redundant.\n\nExample:\n\n\n List x = Collections.unmodifiableList(Collections.singletonList(\"abc\"));\n\nAfter the quick-fix is applied:\n\n\n List x = Collections.singletonList(\"abc\");\n\nIn order to detect the methods that return unmodifiable collections, the\ninspection uses the `org.jetbrains.annotations.Unmodifiable`\nand `org.jetbrains.annotations.UnmodifiableView` annotations.\nUse them to extend the inspection to your own unmodifiable collection\nwrappers.\n\nNew in 2020.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantUnmodifiable", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonExceptionNameEndsWithException", + "shortDescription": { + "text": "Non-exception class name ends with 'Exception'" + }, + "fullDescription": { + "text": "Reports non-'exception' classes whose names end with 'Exception'. Such classes may cause confusion by breaking a common naming convention and often indicate that the 'extends Exception' clause is missing. Example: 'public class NotStartedException {}' A quick-fix that renames such classes is available only in the editor.", + "markdown": "Reports non-`exception` classes whose names end with `Exception`.\n\nSuch classes may cause confusion by breaking a common naming convention and\noften indicate that the `extends Exception` clause is missing.\n\n**Example:**\n\n public class NotStartedException {}\n\nA quick-fix that renames such classes is available only in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonExceptionNameEndsWithException", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions/Class", + "index": 71, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConstantValue", + "shortDescription": { + "text": "Constant values" + }, + "fullDescription": { + "text": "Reports expressions and conditions that always produce the same result, like true, false, null, or zero. Such expressions could be replaced with the corresponding constant value. Very often though they signal about a bug in the code. Examples: '// always true\n // root cause: || is used instead of &&\n if (x > 0 || x < 10) {}\n\n System.out.println(str.trim());\n // always false\n // root cause: variable was dereferenced before null-check\n if (str == null) {}' The inspection behavior may be controlled by a number of annotations, such as nullability annotations, '@Contract' annotation, '@Range' annotation and so on. Configure the inspection: Use the Don't report assertions with condition statically proven to be always true option to avoid reporting assertions that were statically proven to be always true. This also includes conditions like 'if (alwaysFalseCondition) throw new IllegalArgumentException();'. Use the Ignore assert statements option to control how the inspection treats 'assert' statements. By default, the option is disabled, which means that the assertions are assumed to be executed (-ea mode). If the option is enabled, the assertions will be completely ignored (-da mode). Use the Warn when constant is stored in variable option to display warnings when variable is used, whose value is known to be a constant. Before IntelliJ IDEA 2022.3, this inspection was part of \"Constant Conditions & Exceptions\" inspection. Now, it split into two inspections: \"Constant Values\" and \"Nullability and data flow problems\".", + "markdown": "Reports expressions and conditions that always produce the same result, like true, false, null, or zero. Such expressions could be replaced with the corresponding constant value. Very often though they signal about a bug in the code.\n\nExamples:\n\n // always true\n // root cause: || is used instead of &&\n if (x > 0 || x < 10) {}\n\n System.out.println(str.trim());\n // always false\n // root cause: variable was dereferenced before null-check\n if (str == null) {}\n\n\nThe inspection behavior may be controlled by a number of annotations, such as\n[nullability](https://www.jetbrains.com/help/idea/nullable-and-notnull-annotations.html) annotations,\n[@Contract](https://www.jetbrains.com/help/idea/contract-annotations.html) annotation,\n`@Range` annotation and so on.\n\nConfigure the inspection:\n\n* Use the **Don't report assertions with condition statically proven to be always true** option to avoid reporting assertions that were statically proven to be always true. This also includes conditions like `if (alwaysFalseCondition) throw new IllegalArgumentException();`.\n* Use the **Ignore assert statements** option to control how the inspection treats `assert` statements. By default, the option is disabled, which means that the assertions are assumed to be executed (-ea mode). If the option is enabled, the assertions will be completely ignored (-da mode).\n* Use the **Warn when constant is stored in variable** option to display warnings when variable is used, whose value is known to be a constant.\n\n\nBefore IntelliJ IDEA 2022.3, this inspection was part of \"Constant Conditions \\& Exceptions\" inspection. Now, it split into two inspections:\n\"Constant Values\" and \"Nullability and data flow problems\"." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ConstantValue", + "cweIds": [ + 570, + 571 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CastCanBeReplacedWithVariable", + "shortDescription": { + "text": "Cast can be replaced with variable" + }, + "fullDescription": { + "text": "Reports type cast operations that can be replaced with existing local or pattern variables with the same value. Example: 'void foo(Object obj) {\n String s = (String) obj;\n System.out.println(((String) obj).trim());\n }' After the quick-fix is applied: 'void foo(Object obj) {\n String s = (String) obj;\n System.out.println(s.trim());\n }' New in 2022.3", + "markdown": "Reports type cast operations that can be replaced with existing local or pattern variables with the same value.\n\nExample:\n\n\n void foo(Object obj) {\n String s = (String) obj;\n System.out.println(((String) obj).trim());\n }\n\nAfter the quick-fix is applied:\n\n\n void foo(Object obj) {\n String s = (String) obj;\n System.out.println(s.trim());\n }\n\nNew in 2022.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "CastCanBeReplacedWithVariable", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Convert2Diamond", + "shortDescription": { + "text": "Explicit type can be replaced with '<>'" + }, + "fullDescription": { + "text": "Reports 'new' expressions with type arguments that can be replaced a with diamond type '<>'. Example: 'List list = new ArrayList(); // reports array list type argument' After the quick-fix is applied: 'List list = new ArrayList<>();' This inspection only reports if the language level of the project or module is 7 or higher.", + "markdown": "Reports `new` expressions with type arguments that can be replaced a with diamond type `<>`.\n\nExample:\n\n\n List list = new ArrayList(); // reports array list type argument\n\nAfter the quick-fix is applied:\n\n\n List list = new ArrayList<>();\n\nThis inspection only reports if the language level of the project or module is 7 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "Convert2Diamond", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 7", + "index": 112, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "VarargParameter", + "shortDescription": { + "text": "Varargs method" + }, + "fullDescription": { + "text": "Reports methods that accept an arbitrary number of parameters (also known as varargs methods). Example: 'enum EnumConstants {\n A(null), B, C;\n\n EnumConstants(String... ss) {}\n}' A quick-fix is available to replace a variable argument parameter with an equivalent array parameter. Relevant arguments in method calls are wrapped in an array initializer expression. After the quick-fix is applied: 'enum EnumConstants {\n A(null), B(new String[]{}), C(new String[]{});\n\n EnumConstants(String[] ss) {}\n}' Varargs method appeared in Java 5. This inspection can help to downgrade for backward compatibility with earlier Java versions.", + "markdown": "Reports methods that accept an arbitrary number of parameters (also known as varargs methods).\n\n**Example:**\n\n\n enum EnumConstants {\n A(null), B, C;\n\n EnumConstants(String... ss) {}\n }\n\nA quick-fix is available to replace a variable argument\nparameter with an equivalent array parameter. Relevant arguments in method calls are wrapped in an array initializer expression.\nAfter the quick-fix is applied:\n\n\n enum EnumConstants {\n A(null), B(new String[]{}), C(new String[]{});\n\n EnumConstants(String[] ss) {}\n }\n\n\n*Varargs method* appeared in Java 5.\nThis inspection can help to downgrade for backward compatibility with earlier Java versions." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "VariableArgumentMethod", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level issues", + "index": 64, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassComplexity", + "shortDescription": { + "text": "Overly complex class" + }, + "fullDescription": { + "text": "Reports classes whose total complexity exceeds the specified maximum. The total complexity of a class is the sum of cyclomatic complexities of all the methods and initializers the class declares. Inherited methods and initializers are not counted toward the total complexity. Too high complexity indicates that the class should be refactored into several smaller classes. Use the Cyclomatic complexity limit field below to specify the maximum allowed complexity for a class.", + "markdown": "Reports classes whose total complexity exceeds the specified maximum.\n\nThe total complexity of a class is the sum of cyclomatic complexities of all the methods\nand initializers the class declares. Inherited methods and initializers are not counted\ntoward the total complexity.\n\nToo high complexity indicates that the class should be refactored into several smaller classes.\n\nUse the **Cyclomatic complexity limit** field below to specify the maximum allowed complexity for a class." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OverlyComplexClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class metrics", + "index": 87, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SystemGC", + "shortDescription": { + "text": "Call to 'System.gc()' or 'Runtime.gc()'" + }, + "fullDescription": { + "text": "Reports 'System.gc()' or 'Runtime.gc()' calls. While occasionally useful in testing, explicitly triggering garbage collection via 'System.gc()' is almost never recommended in production code and can result in serious performance issues.", + "markdown": "Reports `System.gc()` or `Runtime.gc()` calls. While occasionally useful in testing, explicitly triggering garbage collection via `System.gc()` is almost never recommended in production code and can result in serious performance issues." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToSystemGC", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Memory", + "index": 73, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ArrayLengthInLoopCondition", + "shortDescription": { + "text": "Array.length in loop condition" + }, + "fullDescription": { + "text": "Reports accesses to the '.length' property of an array in the condition part of a loop statement. In highly resource constrained environments, such calls may have adverse performance implications. This inspection is intended for Java ME and other highly resource constrained environments. Applying the results of this inspection without consideration might have negative effects on code clarity and design. Example: 'void foo(Object[] x) {\n for (int i = 0; i < x.length; i++) { /**/ }\n }'", + "markdown": "Reports accesses to the `.length` property of an array in the condition part of a loop statement. In highly resource constrained environments, such calls may have adverse performance implications.\n\n\nThis inspection is intended for Java ME and other highly resource constrained environments.\nApplying the results of this inspection without consideration might have negative effects on code clarity and design.\n\n**Example:**\n\n\n void foo(Object[] x) {\n for (int i = 0; i < x.length; i++) { /**/ }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ArrayLengthInLoopCondition", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance/Embedded", + "index": 19, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CheckForOutOfMemoryOnLargeArrayAllocation", + "shortDescription": { + "text": "Large array allocation with no OutOfMemoryError check" + }, + "fullDescription": { + "text": "Reports large array allocations which do not check for 'java.lang.OutOfMemoryError'. In memory constrained environments, allocations of large data objects should probably be checked for memory depletion. This inspection is intended for Java ME and other highly resource constrained environments. Applying the results of this inspection without consideration might have negative effects on code clarity and design. Use the option to specify the maximum number of elements to allow in unchecked array allocations.", + "markdown": "Reports large array allocations which do not check for `java.lang.OutOfMemoryError`. In memory constrained environments, allocations of large data objects should probably be checked for memory depletion.\n\n\nThis inspection is intended for Java ME and other highly resource constrained environments.\nApplying the results of this inspection without consideration might have negative effects on code clarity and design.\n\n\nUse the option to specify the maximum number of elements to allow in unchecked array allocations." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CheckForOutOfMemoryOnLargeArrayAllocation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance/Embedded", + "index": 19, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassWithoutConstructor", + "shortDescription": { + "text": "Class without constructor" + }, + "fullDescription": { + "text": "Reports classes without constructors. Some coding standards prohibit such classes.", + "markdown": "Reports classes without constructors.\n\nSome coding standards prohibit such classes." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassWithoutConstructor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/JavaBeans issues", + "index": 33, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OverflowingLoopIndex", + "shortDescription": { + "text": "Loop executes zero or billions of times" + }, + "fullDescription": { + "text": "Reports loops that cannot be completed without an index overflow or loops that don't loop at all. It usually happens because of a mistake in the update operation. Example: 'void foo(int s) {\n for (int i = s; i > 12; i++) { // i-- should be here\n System.out.println(i);\n }\n }' New in 2019.1", + "markdown": "Reports loops that cannot be completed without an index overflow or loops that don't loop at all. It usually happens because of a mistake in the update operation.\n\nExample:\n\n\n void foo(int s) {\n for (int i = s; i > 12; i++) { // i-- should be here\n System.out.println(i);\n }\n }\n\nNew in 2019.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "OverflowingLoopIndex", + "cweIds": [ + 691, + 835 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SetReplaceableByEnumSet", + "shortDescription": { + "text": "'Set' can be replaced with 'EnumSet'" + }, + "fullDescription": { + "text": "Reports instantiations of 'java.util.Set' objects whose content types are enumerated classes. Such 'Set' objects can be replaced with 'java.util.EnumSet' objects. 'EnumSet' implementations can be much more efficient compared to other sets, as the underlying data structure is a bit vector. Use the quick-fix to replace the initializer with a call to 'EnumSet.noneOf()'. This quick-fix is not available when the type of the variable is a sub-class of 'Set'. Example: 'enum MyEnum { FOO, BAR; }\n\n Set enums = new HashSet();' After the quick-fix is applied: 'enum MyEnum { FOO, BAR; }\n\n Set enums = EnumSet.noneOf(MyEnum.class);'", + "markdown": "Reports instantiations of `java.util.Set` objects whose content types are enumerated classes. Such `Set` objects can be replaced with `java.util.EnumSet` objects.\n\n\n`EnumSet` implementations can be much more efficient compared to\nother sets, as the underlying data structure is a bit vector. Use the quick-fix to replace the initializer with a call to\n`EnumSet.noneOf()`. This quick-fix is not available when the type of the variable is a sub-class of `Set`.\n\n**Example:**\n\n\n enum MyEnum { FOO, BAR; }\n\n Set enums = new HashSet();\n\nAfter the quick-fix is applied:\n\n\n enum MyEnum { FOO, BAR; }\n\n Set enums = EnumSet.noneOf(MyEnum.class);\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SetReplaceableByEnumSet", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InnerClassMayBeStatic", + "shortDescription": { + "text": "Inner class may be 'static'" + }, + "fullDescription": { + "text": "Reports inner classes that can be made 'static'. A 'static' inner class does not keep an implicit reference to its enclosing instance. This prevents a common cause of memory leaks and uses less memory per instance of the class. Example: 'public class Outer {\n class Inner { // not static\n public void foo() {\n bar(\"x\");\n }\n\n private void bar(String string) {}\n }\n }' After the quick-fix is applied: 'public class Outer {\n static class Inner {\n public void foo() {\n bar(\"x\");\n }\n\n private void bar(String string) {}\n }\n }'", + "markdown": "Reports inner classes that can be made `static`.\n\nA `static` inner class does not keep an implicit reference to its enclosing instance.\nThis prevents a common cause of memory leaks and uses less memory per instance of the class.\n\n**Example:**\n\n\n public class Outer {\n class Inner { // not static\n public void foo() {\n bar(\"x\");\n }\n\n private void bar(String string) {}\n }\n }\n\nAfter the quick-fix is applied:\n\n\n public class Outer {\n static class Inner {\n public void foo() {\n bar(\"x\");\n }\n\n private void bar(String string) {}\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "InnerClassMayBeStatic", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Memory", + "index": 73, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java9UndeclaredServiceUsage", + "shortDescription": { + "text": "Usage of service not declared in 'module-info'" + }, + "fullDescription": { + "text": "Reports situations in which a service is loaded with 'java.util.ServiceLoader' but it isn't declared with the 'uses' clause in the 'module-info.java' file and suggests inserting it. New in 2018.1", + "markdown": "Reports situations in which a service is loaded with `java.util.ServiceLoader` but it isn't declared with the `uses` clause in the `module-info.java` file and suggests inserting it.\n\nNew in 2018.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "Java9UndeclaredServiceUsage", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Visibility", + "index": 57, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PackageInfoWithoutPackage", + "shortDescription": { + "text": "'package-info.java' without 'package' statement" + }, + "fullDescription": { + "text": "Reports 'package-info.java' files without a 'package' statement. The Javadoc tool considers such files documentation for the default package even when the file is located somewhere else.", + "markdown": "Reports `package-info.java` files without a `package` statement.\n\n\nThe Javadoc tool considers such files documentation for the default package even when the file is located somewhere else." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PackageInfoWithoutPackage", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Javadoc", + "index": 45, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CallToSimpleSetterInClass", + "shortDescription": { + "text": "Call to simple setter from within class" + }, + "fullDescription": { + "text": "Reports calls to a simple property setter from within the property's class. A simple property setter is defined as one which simply assigns the value of its parameter to a field, and does no other calculations. Such simple setter calls can be safely inlined. Some coding standards also suggest against the use of simple setters for code clarity reasons. Example: 'class Foo {\n private int index;\n public Foo(int idx) {\n setIndex(idx);\n }\n public void setIndex(int idx) {\n index = idx;\n }\n }' After the quick-fix is applied: 'class Foo {\n private int index;\n public Foo(int idx) {\n index = idx;\n }\n public void setIndex(int idx) {\n index = idx;\n }\n }' Use the following options to configure the inspection: Whether to only report setter calls on 'this', not on objects of the same type passed in as a parameter. Whether to ignore non-'private' setters.", + "markdown": "Reports calls to a simple property setter from within the property's class.\n\n\nA simple property setter is defined as one which simply assigns the value of its parameter to a field,\nand does no other calculations. Such simple setter calls can be safely inlined.\nSome coding standards also suggest against the use of simple setters for code clarity reasons.\n\n**Example:**\n\n\n class Foo {\n private int index;\n public Foo(int idx) {\n setIndex(idx);\n }\n public void setIndex(int idx) {\n index = idx;\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo {\n private int index;\n public Foo(int idx) {\n index = idx;\n }\n public void setIndex(int idx) {\n index = idx;\n }\n }\n\nUse the following options to configure the inspection:\n\n* Whether to only report setter calls on `this`, not on objects of the same type passed in as a parameter.\n* Whether to ignore non-`private` setters." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToSimpleSetterFromWithinClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonSerializableWithSerializationMethods", + "shortDescription": { + "text": "Non-serializable class with 'readObject()' or 'writeObject()'" + }, + "fullDescription": { + "text": "Reports non-'Serializable' classes that define 'readObject()' or 'writeObject()' methods. Such methods in that context normally indicate an error. Example: 'public class SampleClass {\n private void readObject(ObjectInputStream str) {}\n private void writeObject(ObjectOutputStream str) {}\n }'", + "markdown": "Reports non-`Serializable` classes that define `readObject()` or `writeObject()` methods. Such methods in that context normally indicate an error.\n\n**Example:**\n\n\n public class SampleClass {\n private void readObject(ObjectInputStream str) {}\n private void writeObject(ObjectOutputStream str) {}\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonSerializableClassWithSerializationMethods", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CloneReturnsClassType", + "shortDescription": { + "text": "'clone()' should have return type equal to the class it contains" + }, + "fullDescription": { + "text": "Reports 'clone()' methods with return types different from the class they're located in. Often a 'clone()' method will have a return type of 'java.lang.Object', which makes it harder to use by its clients. Effective Java (the second and third editions) recommends making the return type of the 'clone()' method the same as the class type of the object it returns. Example: 'class Foo implements Cloneable {\n public Object clone() {\n try {\n return super.clone();\n } catch (CloneNotSupportedException e) {\n throw new AssertionError();\n }\n }\n }' After the quick-fix is applied: 'class Foo implements Cloneable {\n public Foo clone() {\n try {\n return (Foo)super.clone();\n } catch (CloneNotSupportedException e) {\n throw new AssertionError();\n }\n }\n }'", + "markdown": "Reports `clone()` methods with return types different from the class they're located in.\n\nOften a `clone()` method will have a return type of `java.lang.Object`, which makes it harder to use by its clients.\n*Effective Java* (the second and third editions) recommends making the return type of the `clone()` method the same as the\nclass type of the object it returns.\n\n**Example:**\n\n\n class Foo implements Cloneable {\n public Object clone() {\n try {\n return super.clone();\n } catch (CloneNotSupportedException e) {\n throw new AssertionError();\n }\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo implements Cloneable {\n public Foo clone() {\n try {\n return (Foo)super.clone();\n } catch (CloneNotSupportedException e) {\n throw new AssertionError();\n }\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CloneReturnsClassType", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Cloning issues", + "index": 82, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OptionalToIf", + "shortDescription": { + "text": "'Optional' can be replaced with sequence of 'if' statements" + }, + "fullDescription": { + "text": "Reports 'Optional' call chains that can be replaced with a sequence of 'if' statements. Example: 'return Optional.ofNullable(name)\n .map(this::extractInitials)\n .map(initials -> initials.toUpperCase(Locale.ENGLISH))\n .orElseGet(this::getDefault);' After the quick-fix is applied: 'if (name != null) {\n String initials = extractInitials(name);\n if (initials != null) return initials.toUpperCase(Locale.ENGLISH);\n }\n return getDefault();' 'java.util.Optional' appeared in Java 8. This inspection can help to downgrade for backward compatibility with earlier Java versions. New in 2020.2", + "markdown": "Reports `Optional` call chains that can be replaced with a sequence of `if` statements.\n\nExample:\n\n\n return Optional.ofNullable(name)\n .map(this::extractInitials)\n .map(initials -> initials.toUpperCase(Locale.ENGLISH))\n .orElseGet(this::getDefault);\n\nAfter the quick-fix is applied:\n\n\n if (name != null) {\n String initials = extractInitials(name);\n if (initials != null) return initials.toUpperCase(Locale.ENGLISH);\n }\n return getDefault();\n\n\n`java.util.Optional` appeared in Java 8.\nThis inspection can help to downgrade for backward compatibility with earlier Java versions.\n\nNew in 2020.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "OptionalToIf", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryFinalOnLocalVariableOrParameter", + "shortDescription": { + "text": "Unnecessary 'final' on local variable or parameter" + }, + "fullDescription": { + "text": "Reports local variables or parameters unnecessarily declared 'final'. Some coding standards frown upon variables declared 'final' for reasons of terseness. Example: 'class Foo {\n Foo(Object o) {}\n\n void bar(final Object o) {\n new Foo(o);\n }\n }' After the quick-fix is applied: 'class Foo {\n Foo(Object o) {}\n\n void bar(Object o) {\n new Foo(o);\n }\n }' Use the inspection options to toggle the reporting for: local variables parameters (including parameters of 'catch' blocks and enhanced 'for' statements) Also, you can configure the inspection to only report 'final' parameters of 'abstract' or interface methods, which may be considered extra unnecessary as such markings don't affect the implementation of these methods.", + "markdown": "Reports local variables or parameters unnecessarily declared `final`.\n\nSome coding standards frown upon variables declared `final` for reasons of terseness.\n\n**Example:**\n\n\n class Foo {\n Foo(Object o) {}\n\n void bar(final Object o) {\n new Foo(o);\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo {\n Foo(Object o) {}\n\n void bar(Object o) {\n new Foo(o);\n }\n }\n\n\nUse the inspection options to toggle the reporting for:\n\n* local variables\n* parameters (including parameters of `catch` blocks and enhanced `for` statements)\n\n\nAlso, you can configure the inspection to only report `final` parameters of `abstract` or interface\nmethods, which may be considered extra unnecessary as such markings don't\naffect the implementation of these methods." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryFinalOnLocalVariableOrParameter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IllegalDependencyOnInternalPackage", + "shortDescription": { + "text": "Illegal dependency on internal package" + }, + "fullDescription": { + "text": "Reports references in modules without 'module-info.java' on packages which are not exported from named modules. Such configuration may occur when some modules in the project are already migrated to Java modules but others are still non-modular. By analogy to the JDK, such non-modular code should not get access to the code in named modules which is not explicitly exported.", + "markdown": "Reports references in modules without `module-info.java` on packages which are not exported from named modules.\n\nSuch configuration may occur when some modules in the project are already migrated to Java modules but others are still non-modular.\nBy analogy to the JDK, such non-modular code should not get access to the code in named modules which is not explicitly exported." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "IllegalDependencyOnInternalPackage", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages", + "index": 2, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonBooleanMethodNameMayNotStartWithQuestion", + "shortDescription": { + "text": "Non-boolean method name must not start with question word" + }, + "fullDescription": { + "text": "Reports non-boolean methods whose names start with a question word. Such method names may be confusing. Non-boolean methods that override library methods are ignored by this inspection. Example: 'public void hasName(String name) {\n assert names.contains(name);\n }' A quick-fix that renames such methods is available only in the editor. Configure the inspection: Use the Boolean method name prefixes list to specify the question words that should be used only for boolean methods. Use the Ignore methods with 'java.lang.Boolean' return type option to ignore methods with 'java.lang.Boolean' return type. Use the Ignore methods overriding/implementing a super method option to ignore methods which have supers.", + "markdown": "Reports non-boolean methods whose names start with a question word. Such method names may be confusing.\n\nNon-boolean methods that override library methods are ignored by this inspection.\n\n**Example:**\n\n\n public void hasName(String name) {\n assert names.contains(name);\n }\n\nA quick-fix that renames such methods is available only in the editor.\n\nConfigure the inspection:\n\n* Use the **Boolean method name prefixes** list to specify the question words that should be used only for boolean methods.\n* Use the **Ignore methods with 'java.lang.Boolean' return type** option to ignore methods with `java.lang.Boolean` return type.\n* Use the **Ignore methods overriding/implementing a super method** option to ignore methods which have supers." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonBooleanMethodNameMayNotStartWithQuestion", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions/Method", + "index": 88, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "WeakerAccess", + "shortDescription": { + "text": "Declaration access can be weaker" + }, + "fullDescription": { + "text": "Reports fields, methods or classes that may have their access modifier narrowed down. Example: 'class Sample {\n void foo() {\n bar(\"foo\", \"foo\");\n }\n void bar(String x, String y) { } // can be private\n }' After the quick-fix is applied: 'class Sample {\n void foo() {\n bar(\"foo\", \"foo\");\n }\n private void bar(String x, String y) { }\n }' Use the inspection's options to define the rules for the modifier change suggestions.", + "markdown": "Reports fields, methods or classes that may have their access modifier narrowed down.\n\nExample:\n\n\n class Sample {\n void foo() {\n bar(\"foo\", \"foo\");\n }\n void bar(String x, String y) { } // can be private\n }\n\nAfter the quick-fix is applied:\n\n\n class Sample {\n void foo() {\n bar(\"foo\", \"foo\");\n }\n private void bar(String x, String y) { }\n }\n\nUse the inspection's options to define the rules for the modifier change suggestions." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "WeakerAccess", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceWithJavadoc", + "shortDescription": { + "text": "Comment replaceable with Javadoc" + }, + "fullDescription": { + "text": "Reports a regular comment that belongs to a field, method, or class that can be replaced with a Javadoc comment. Example: 'public class Main {\n /*\n * Hello,\n */\n // World!\n void f() {\n }\n }' After the quick-fix is applied: 'public class Main {\n /**\n * Hello,\n * World!\n */\n void f() {\n }\n }'", + "markdown": "Reports a regular comment that belongs to a field, method, or class that can be replaced with a Javadoc comment.\n\n**Example:**\n\n\n public class Main {\n /*\n * Hello,\n */\n // World!\n void f() {\n }\n }\n\nAfter the quick-fix is applied:\n\n\n public class Main {\n /**\n * Hello,\n * World!\n */\n void f() {\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceWithJavadoc", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Javadoc", + "index": 45, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BigDecimalMethodWithoutRoundingCalled", + "shortDescription": { + "text": "Call to 'BigDecimal' method without a rounding mode argument" + }, + "fullDescription": { + "text": "Reports calls to 'divide()' or 'setScale()' without a rounding mode argument. Such calls can lead to an 'ArithmeticException' when the exact value cannot be represented in the result (for example, because it has a non-terminating decimal expansion). Specifying a rounding mode prevents the 'ArithmeticException'. Example: 'BigDecimal.valueOf(1).divide(BigDecimal.valueOf(3));'", + "markdown": "Reports calls to `divide()` or `setScale()` without a rounding mode argument.\n\nSuch calls can lead to an `ArithmeticException` when the exact value cannot be represented in the result\n(for example, because it has a non-terminating decimal expansion).\n\nSpecifying a rounding mode prevents the `ArithmeticException`.\n\n**Example:**\n\n\n BigDecimal.valueOf(1).divide(BigDecimal.valueOf(3));\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "BigDecimalMethodWithoutRoundingCalled", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OverlyComplexArithmeticExpression", + "shortDescription": { + "text": "Overly complex arithmetic expression" + }, + "fullDescription": { + "text": "Reports arithmetic expressions with the excessive number of terms. Such expressions might be hard to understand and might contain errors. Parameters, field references, and other primary expressions are counted as a term. Example: 'int calc(int a, int b) {\n return a + a + a + b + b + b + b; // The line contains 7 terms and will be reported.\n }' Use the field below to specify a number of terms allowed in arithmetic expressions.", + "markdown": "Reports arithmetic expressions with the excessive number of terms. Such expressions might be hard to understand and might contain errors.\n\nParameters, field references, and other primary expressions are counted as a term.\n\n**Example:**\n\n int calc(int a, int b) {\n return a + a + a + b + b + b + b; // The line contains 7 terms and will be reported.\n }\n\nUse the field below to specify a number of terms allowed in arithmetic expressions." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OverlyComplexArithmeticExpression", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Numeric issues", + "index": 27, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DriverManagerGetConnection", + "shortDescription": { + "text": "Use of 'DriverManager' to get JDBC connection" + }, + "fullDescription": { + "text": "Reports any uses of 'java.sql.DriverManager' to acquire a JDBC connection. 'java.sql.DriverManager' has been superseded by 'javax.sql.Datasource', which allows for connection pooling and other optimizations. Example: 'Connection conn = DriverManager.getConnection(url, username, password);'", + "markdown": "Reports any uses of `java.sql.DriverManager` to acquire a JDBC connection.\n\n\n`java.sql.DriverManager`\nhas been superseded by `javax.sql.Datasource`, which\nallows for connection pooling and other optimizations.\n\n**Example:**\n\n Connection conn = DriverManager.getConnection(url, username, password);\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToDriverManagerGetConnection", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Resource management", + "index": 47, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "WrapperTypeMayBePrimitive", + "shortDescription": { + "text": "Wrapper type may be primitive" + }, + "fullDescription": { + "text": "Reports local variables of wrapper type that are mostly used as primitive types. In some cases, boxing can be source of significant performance penalty, especially in loops. Heuristics are applied to estimate the number of boxing operations. For example, conversions inside loops are considered as much more numerous. Example: 'public void example() {\n Integer value = 12;\n needBox(value);\n for (int i = 0; i < 10; i++) {\n // Loop usages considered as happening more often\n needPrimitive(value);\n }\n }\n\n void needPrimitive(int value) {}\n void needBox(Integer value) {}' After the quick-fix is applied: 'public void example() {\n int value = 12;\n needBox(value);\n for (int i = 0; i < 10; i++) {\n // Loop usages considered as happening more often\n needPrimitive(value);\n }\n }\n\n void needPrimitive(int value) {}\n void needBox(Integer value) {}' New in 2018.2", + "markdown": "Reports local variables of wrapper type that are mostly used as primitive types.\n\nIn some cases, boxing can be source of significant performance penalty, especially in loops.\n\nHeuristics are applied to estimate the number of boxing operations. For example, conversions inside loops are considered\nas much more numerous.\n\n**Example:**\n\n public void example() {\n Integer value = 12;\n needBox(value);\n for (int i = 0; i < 10; i++) {\n // Loop usages considered as happening more often\n needPrimitive(value);\n }\n }\n\n void needPrimitive(int value) {}\n void needBox(Integer value) {}\n\nAfter the quick-fix is applied:\n\n public void example() {\n int value = 12;\n needBox(value);\n for (int i = 0; i < 10; i++) {\n // Loop usages considered as happening more often\n needPrimitive(value);\n }\n }\n\n void needPrimitive(int value) {}\n void needBox(Integer value) {}\n\n\nNew in 2018.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "WrapperTypeMayBePrimitive", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EmptyStatementBody", + "shortDescription": { + "text": "Statement with empty body" + }, + "fullDescription": { + "text": "Reports 'if', 'while', 'do', 'for', and 'switch' statements with empty bodies. While occasionally intended, such code is confusing and is often the result of a typo. This inspection is disabled in JSP files.", + "markdown": "Reports `if`, `while`, `do`, `for`, and `switch` statements with empty bodies.\n\nWhile occasionally intended, such code is confusing and is often the result of a typo.\n\nThis inspection is disabled in JSP files." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "StatementWithEmptyBody", + "cweIds": [ + 561 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EmptyFinallyBlock", + "shortDescription": { + "text": "Empty 'finally' block" + }, + "fullDescription": { + "text": "Reports empty 'finally' blocks. Empty 'finally' blocks usually indicate coding errors. They may also remain after code refactoring and can safely be removed. This inspection doesn't report empty 'finally' blocks found in JSP files. Example: 'try {\n Files.readString(Paths.get(\"in.txt\"));\n } catch (IOException e) {\n throw new RuntimeException(e);\n } finally {\n\n }' After the quick-fix is applied: 'try {\n Files.readString(Paths.get(\"in.txt\"));\n } catch (IOException e) {\n throw new RuntimeException(e);\n }'", + "markdown": "Reports empty `finally` blocks.\n\nEmpty `finally` blocks usually indicate coding errors. They may also remain after code refactoring and can safely be removed.\n\nThis inspection doesn't report empty `finally` blocks found in JSP files.\n\n**Example:**\n\n\n try {\n Files.readString(Paths.get(\"in.txt\"));\n } catch (IOException e) {\n throw new RuntimeException(e);\n } finally {\n\n }\n\nAfter the quick-fix is applied:\n\n\n try {\n Files.readString(Paths.get(\"in.txt\"));\n } catch (IOException e) {\n throw new RuntimeException(e);\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "EmptyFinallyBlock", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InconsistentLanguageLevel", + "shortDescription": { + "text": "Inconsistent language level settings" + }, + "fullDescription": { + "text": "Reports modules which depend on other modules with a higher language level. Such dependencies should be removed or the language level of the module be increased. Available only from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name and isn't reported in the editor.", + "markdown": "Reports modules which depend on other modules with a higher language level.\n\nSuch dependencies should be removed or the language level of the module be increased.\n\nAvailable only from **Code \\| Inspect Code** or\n**Code \\| Analyze Code \\| Run Inspection by Name** and isn't reported in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "InconsistentLanguageLevel", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Modularization issues", + "index": 69, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EnumerationCanBeIteration", + "shortDescription": { + "text": "Enumeration can be iteration" + }, + "fullDescription": { + "text": "Reports calls to 'Enumeration' methods that are used on collections and may be replaced with equivalent 'Iterator' constructs. Example: 'Enumeration keys = map.keys();\n while (keys.hasMoreElements()) {\n String name = keys.nextElement();\n }' After the quick-fix is applied: 'Iterator iterator = map.keySet().iterator();\n while (iterator.hasNext()) {\n String name = iterator.next();\n }'", + "markdown": "Reports calls to `Enumeration` methods that are used on collections and may be replaced with equivalent `Iterator` constructs.\n\n**Example:**\n\n\n Enumeration keys = map.keys();\n while (keys.hasMoreElements()) {\n String name = keys.nextElement();\n }\n\nAfter the quick-fix is applied:\n\n\n Iterator iterator = map.keySet().iterator();\n while (iterator.hasNext()) {\n String name = iterator.next();\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EnumerationCanBeIteration", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids", + "index": 52, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ThreadDumpStack", + "shortDescription": { + "text": "Call to 'Thread.dumpStack()'" + }, + "fullDescription": { + "text": "Reports usages of 'Thread.dumpStack()'. Such statements are often used for temporary debugging and should be either removed from the production code or replaced with a more robust logging facility.", + "markdown": "Reports usages of `Thread.dumpStack()`.\n\nSuch statements are often used for temporary debugging and should be either removed from the production code\nor replaced with a more robust logging facility." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToThreadDumpStack", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code maturity", + "index": 50, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryBlockStatement", + "shortDescription": { + "text": "Unnecessary code block" + }, + "fullDescription": { + "text": "Reports code blocks that are redundant to the semantics of the program and can be replaced with their contents. The code blocks that are the bodies of 'if', 'do', 'while', or 'for' statements will not be reported by this inspection. Example: 'void foo() {\n { // unnecessary\n int result = call();\n analyze(result);\n } // unnecessary\n }' Configure the inspection: Use the Ignore branches of 'switch' statements option to ignore the code blocks that are used as branches of switch statements.", + "markdown": "Reports code blocks that are redundant to the semantics of the program and can be replaced with their contents.\n\nThe code blocks that are the bodies of `if`, `do`,\n`while`, or `for` statements will not be reported by this\ninspection.\n\nExample:\n\n\n void foo() {\n { // unnecessary\n int result = call();\n analyze(result);\n } // unnecessary\n }\n\nConfigure the inspection:\n\n\nUse the **Ignore branches of 'switch' statements** option to ignore the code blocks that are used as branches of switch statements." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "UnnecessaryCodeBlock", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FinalMethodInFinalClass", + "shortDescription": { + "text": "'final' method in 'final' class" + }, + "fullDescription": { + "text": "Reports 'final' methods in 'final' classes. Since 'final' classes cannot be inherited, marking a method as 'final' may be unnecessary and confusing. Example: 'record Bar(int a, int b) {\n public final int sum() { \n return a + b;\n }\n}'\n After the quick-fix is applied: 'record Bar(int a, int b) {\n public int sum() { \n return a + b;\n }\n}' As shown in the example, a class can be marked as 'final' explicitly or implicitly.", + "markdown": "Reports `final` methods in `final` classes.\n\nSince `final` classes cannot be inherited, marking a method as `final`\nmay be unnecessary and confusing.\n\n**Example:**\n\n record Bar(int a, int b) {\n public final int sum() { \n return a + b;\n }\n }\n\nAfter the quick-fix is applied:\n\n record Bar(int a, int b) {\n public int sum() { \n return a + b;\n }\n }\n\nAs shown in the example, a class can be marked as `final` explicitly or implicitly." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "FinalMethodInFinalClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FinalPrivateMethod", + "shortDescription": { + "text": "'private' method declared 'final'" + }, + "fullDescription": { + "text": "Reports methods that are marked with both 'final' and 'private' keywords. Since 'private' methods cannot be meaningfully overridden because of their visibility, declaring them 'final' is redundant.", + "markdown": "Reports methods that are marked with both `final` and `private` keywords.\n\nSince `private` methods cannot be meaningfully overridden because of their visibility, declaring them\n`final` is redundant." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "FinalPrivateMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FinalStaticMethod", + "shortDescription": { + "text": "'static' method declared 'final'" + }, + "fullDescription": { + "text": "Reports static methods that are marked as 'final'. Such code might indicate an error or an incorrect assumption about the effect of the 'final' keyword. Static methods are not subject to runtime polymorphism, so the only purpose of the 'final' keyword used with static methods is to ensure the method will not be hidden in a subclass.", + "markdown": "Reports static methods that are marked as `final`.\n\nSuch code might indicate an error or an incorrect assumption about the effect of the `final` keyword.\nStatic methods are not subject to runtime polymorphism, so the only purpose of the `final` keyword used with static methods\nis to ensure the method will not be hidden in a subclass." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "FinalStaticMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantTypeArguments", + "shortDescription": { + "text": "Redundant type arguments" + }, + "fullDescription": { + "text": "Reports calls to parametrized methods with explicit argument types that can be omitted since they will be unambiguously inferred by the compiler. Using redundant type arguments is unnecessary and makes the code less readable. Example: 'List list = Arrays.asList(\"Hello\", \"World\");' A quick-fix is provided to remove redundant type arguments: 'List list = Arrays.asList(\"Hello\", \"World\");'", + "markdown": "Reports calls to parametrized methods with explicit argument types that can be omitted since they will be unambiguously inferred by the compiler.\n\n\nUsing redundant type arguments is unnecessary and makes the code less readable.\n\nExample:\n\n\n List list = Arrays.asList(\"Hello\", \"World\");\n\nA quick-fix is provided to remove redundant type arguments:\n\n\n List list = Arrays.asList(\"Hello\", \"World\");\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantTypeArguments", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FieldHasSetterButNoGetter", + "shortDescription": { + "text": "Field has setter but no getter" + }, + "fullDescription": { + "text": "Reports fields that have setter methods but no getter methods. In certain bean containers, when used within the Java beans specification, such fields might be difficult to work with.", + "markdown": "Reports fields that have setter methods but no getter methods.\n\n\nIn certain bean containers, when used within the Java beans specification, such fields might be difficult\nto work with." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "FieldHasSetterButNoGetter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/JavaBeans issues", + "index": 33, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AssignmentOrReturnOfFieldWithMutableType", + "shortDescription": { + "text": "Assignment or return of field with mutable type" + }, + "fullDescription": { + "text": "Reports return of, or assignment from a method parameter to an array or a mutable type like 'Collection', 'Date', 'Map', 'Calendar', etc. Because such types are mutable, this construct may result in unexpected modifications of an object's state from outside the owning class. Although this construct may be useful for performance reasons, it is inherently prone to bugs. The following mutable types are reported: 'java.util.Date' 'java.util.Calendar' 'java.util.Collection' 'java.util.Map' 'com.google.common.collect.Multimap' 'com.google.common.collect.Table' The quick-fix adds a call to the field's '.clone()' method. Example: 'class Log {\n String[] messages;\n ...\n\n String[] getMessages() {\n return messages; // warning: Return of String[] field 'messages'\n }\n }' After the quick-fix is applied: 'class Log {\n String[] messages;\n ...\n\n String[] getMessages() {\n return messages.clone();\n }\n }' Use the Ignore assignments in and returns from private methods option to ignore assignments and returns in 'private' methods.", + "markdown": "Reports return of, or assignment from a method parameter to an array or a mutable type like `Collection`, `Date`, `Map`, `Calendar`, etc.\n\nBecause such types are mutable, this construct may\nresult in unexpected modifications of an object's state from outside the owning class. Although this construct may be useful for\nperformance reasons, it is inherently prone to bugs.\n\nThe following mutable types are reported:\n\n* `java.util.Date`\n* `java.util.Calendar`\n* `java.util.Collection`\n* `java.util.Map`\n* `com.google.common.collect.Multimap`\n* `com.google.common.collect.Table`\n\nThe quick-fix adds a call to the field's `.clone()` method.\n\n**Example:**\n\n\n class Log {\n String[] messages;\n ...\n\n String[] getMessages() {\n return messages; // warning: Return of String[] field 'messages'\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Log {\n String[] messages;\n ...\n\n String[] getMessages() {\n return messages.clone();\n }\n }\n\nUse the **Ignore assignments in and returns from private methods** option to ignore assignments and returns in `private` methods." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AssignmentOrReturnOfFieldWithMutableType", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Encapsulation", + "index": 60, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RuntimeExecWithNonConstantString", + "shortDescription": { + "text": "Call to 'Runtime.exec()' with non-constant string" + }, + "fullDescription": { + "text": "Reports calls to 'java.lang.Runtime.exec()' which take a dynamically-constructed string as the command to execute. Constructed execution strings are a common source of security breaches. By default, this inspection ignores compile-time constants. Example: 'String i = getUserInput();\n Runtime runtime = Runtime.getRuntime();\n runtime.exec(\"foo\" + i); // reports warning' Use the inspection settings to consider any 'static' 'final' fields as constant. Be careful, because strings like the following will be ignored when the option is enabled: 'static final String COMMAND = \"ping \" + getDomainFromUserInput() + \"'\";'", + "markdown": "Reports calls to `java.lang.Runtime.exec()` which take a dynamically-constructed string as the command to execute.\n\n\nConstructed execution strings are a common source of security breaches.\nBy default, this inspection ignores compile-time constants.\n\n**Example:**\n\n\n String i = getUserInput();\n Runtime runtime = Runtime.getRuntime();\n runtime.exec(\"foo\" + i); // reports warning\n\n\nUse the inspection settings to consider any `static` `final` fields as constant.\nBe careful, because strings like the following will be ignored when the option is enabled:\n\n\n static final String COMMAND = \"ping \" + getDomainFromUserInput() + \"'\";\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToRuntimeExecWithNonConstantString", + "cweIds": [ + 20, + 77, + 78, + 88, + 94 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Security", + "index": 30, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryUnboxing", + "shortDescription": { + "text": "Unnecessary unboxing" + }, + "fullDescription": { + "text": "Reports unboxing, that is explicit unwrapping of wrapped primitive values. Unboxing is unnecessary as of Java 5 and later, and can safely be removed. Examples: 'Integer i = Integer.valueOf(42).intValue();' → 'Integer i = Integer.valueOf(42);' 'int k = Integer.valueOf(42).intValue();' → 'int k = Integer.valueOf(42);' (reports only when the Only report truly superfluously unboxed expressions option is not checked) Use the Only report truly superfluously unboxed expressions option to only report truly superfluous unboxing, where an unboxed value is immediately boxed either implicitly or explicitly. In this case, the entire unboxing-boxing step can be removed. The inspection doesn't report simple explicit unboxing. This inspection only reports if the language level of the project or module is 5 or higher.", + "markdown": "Reports unboxing, that is explicit unwrapping of wrapped primitive values.\n\nUnboxing is unnecessary as of Java 5 and later, and can safely be removed.\n\n**Examples:**\n\n* `Integer i = Integer.valueOf(42).intValue();` → `Integer i = Integer.valueOf(42);`\n* `int k = Integer.valueOf(42).intValue();` → `int k = Integer.valueOf(42);`\n\n (reports only when the **Only report truly superfluously unboxed expressions** option is not checked)\n\n\nUse the **Only report truly superfluously unboxed expressions** option to only report truly superfluous unboxing,\nwhere an unboxed value is immediately boxed either implicitly or explicitly.\nIn this case, the entire unboxing-boxing step can be removed. The inspection doesn't report simple explicit unboxing.\n\nThis inspection only reports if the language level of the project or module is 5 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryUnboxing", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 5", + "index": 53, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OptionalAssignedToNull", + "shortDescription": { + "text": "Null value for Optional type" + }, + "fullDescription": { + "text": "Reports 'null' assigned to 'Optional' variable or returned from method returning 'Optional'. It's recommended that you use 'Optional.empty()' (or 'Optional.absent()' for Guava) to denote an empty value. Example: 'Optional foo(boolean flag) {\n return flag ? Optional.of(42) : null;\n }' After the quick-fix is applied: 'Optional foo(boolean flag) {\n return flag ? Optional.of(42) : Optional.empty();\n }' Configure the inspection: Use the Report comparison of Optional with null option to also report comparisons like 'optional == null'. While in rare cases (e.g. lazily initialized optional field) this might be correct, optional variable is usually never null, and probably 'optional.isPresent()' was intended. This inspection only reports if the language level of the project or module is 8 or higher. New in 2017.2", + "markdown": "Reports `null` assigned to `Optional` variable or returned from method returning `Optional`.\n\nIt's recommended that you use `Optional.empty()` (or `Optional.absent()` for Guava) to denote an empty value.\n\nExample:\n\n\n Optional foo(boolean flag) {\n return flag ? Optional.of(42) : null;\n }\n\nAfter the quick-fix is applied:\n\n\n Optional foo(boolean flag) {\n return flag ? Optional.of(42) : Optional.empty();\n }\n\nConfigure the inspection:\n\n\nUse the **Report comparison of Optional with null** option to also report comparisons like `optional == null`. While in rare cases (e.g. lazily initialized\noptional field) this might be correct, optional variable is usually never null, and probably `optional.isPresent()` was\nintended.\n\nThis inspection only reports if the language level of the project or module is 8 or higher.\n\nNew in 2017.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OptionalAssignedToNull", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code maturity", + "index": 50, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PointlessIndexOfComparison", + "shortDescription": { + "text": "Pointless 'indexOf()' comparison" + }, + "fullDescription": { + "text": "Reports unnecessary comparisons with '.indexOf()' expressions. An example of such an expression is comparing the result of '.indexOf()' with numbers smaller than -1.", + "markdown": "Reports unnecessary comparisons with `.indexOf()` expressions. An example of such an expression is comparing the result of `.indexOf()` with numbers smaller than -1." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PointlessIndexOfComparison", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ErrorRethrown", + "shortDescription": { + "text": "'Error' not rethrown" + }, + "fullDescription": { + "text": "Reports 'try' statements that catch 'java.lang.Error' or any of its subclasses and do not rethrow the error. Statements that catch 'java.lang.ThreadDeath' are not reported. Example: 'try {\n executeTests(request);\n }\n catch (OutOfMemoryError ex) { // warning: Error 'ex' not rethrown\n return false;\n }'", + "markdown": "Reports `try` statements that catch `java.lang.Error` or any of its subclasses and do not rethrow the error.\n\nStatements that catch `java.lang.ThreadDeath` are not\nreported.\n\n**Example:**\n\n\n try {\n executeTests(request);\n }\n catch (OutOfMemoryError ex) { // warning: Error 'ex' not rethrown\n return false;\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ErrorNotRethrown", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EqualsAndHashcode", + "shortDescription": { + "text": "'equals()' and 'hashCode()' not paired" + }, + "fullDescription": { + "text": "Reports classes that override the 'equals()' method but do not override the 'hashCode()' method or vice versa, which can potentially lead to problems when the class is added to a 'Collection' or a 'HashMap'. The quick-fix generates the default implementation for an absent method. Example: 'class StringHolder {\n String s;\n\n @Override public int hashCode() {\n return s != null ? s.hashCode() : 0;\n }\n}' After the quick-fix is applied: 'class StringHolder {\n String s;\n\n @Override public int hashCode() {\n return s != null ? s.hashCode() : 0;\n }\n\n @Override\n public boolean equals(Object o) {\n if (this == o) return true;\n if (!(o instanceof StringHolder)) return false;\n\n StringHolder holder = (StringHolder)o;\n\n if (s != null ? !s.equals(holder.s) : holder.s != null) return false;\n\n return true;\n }\n}'", + "markdown": "Reports classes that override the `equals()` method but do not override the `hashCode()` method or vice versa, which can potentially lead to problems when the class is added to a `Collection` or a `HashMap`.\n\nThe quick-fix generates the default implementation for an absent method.\n\nExample:\n\n\n class StringHolder {\n String s;\n\n @Override public int hashCode() {\n return s != null ? s.hashCode() : 0;\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class StringHolder {\n String s;\n\n @Override public int hashCode() {\n return s != null ? s.hashCode() : 0;\n }\n\n @Override\n public boolean equals(Object o) {\n if (this == o) return true;\n if (!(o instanceof StringHolder)) return false;\n\n StringHolder holder = (StringHolder)o;\n\n if (s != null ? !s.equals(holder.s) : holder.s != null) return false;\n\n return true;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EqualsAndHashcode", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IteratorHasNextCallsIteratorNext", + "shortDescription": { + "text": "'Iterator.hasNext()' which calls 'next()'" + }, + "fullDescription": { + "text": "Reports implementations of 'Iterator.hasNext()' or 'ListIterator.hasPrevious()' that call 'Iterator.next()' or 'ListIterator.previous()' on the iterator instance. Such calls are almost certainly an error, as methods like 'hasNext()' should not modify the iterators state, while 'next()' should. Example: 'class MyIterator implements Iterator {\n public boolean hasNext() {\n return next() != null;\n }\n }'", + "markdown": "Reports implementations of `Iterator.hasNext()` or `ListIterator.hasPrevious()` that call `Iterator.next()` or `ListIterator.previous()` on the iterator instance. Such calls are almost certainly an error, as methods like `hasNext()` should not modify the iterators state, while `next()` should.\n\n**Example:**\n\n\n class MyIterator implements Iterator {\n public boolean hasNext() {\n return next() != null;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "IteratorHasNextCallsIteratorNext", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CyclicPackageDependency", + "shortDescription": { + "text": "Cyclic package dependency" + }, + "fullDescription": { + "text": "Reports packages that are mutually or cyclically dependent on other packages. Such cyclic dependencies make code fragile and hard to maintain. Available only from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name and isn't reported in the editor.", + "markdown": "Reports packages that are mutually or cyclically dependent on other packages.\n\nSuch cyclic dependencies make code fragile and hard to maintain.\n\nAvailable only from **Code \\| Inspect Code** or\n**Code \\| Analyze Code \\| Run Inspection by Name** and isn't reported in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CyclicPackageDependency", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Dependency issues", + "index": 89, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringRepeatCanBeUsed", + "shortDescription": { + "text": "String.repeat() can be used" + }, + "fullDescription": { + "text": "Reports loops that can be replaced with a single 'String.repeat()' method (available since Java 11). Example: 'void append(StringBuilder sb, int count, Object obj) {\n for (int i = 0; i < count; i++) {\n sb.append(obj);\n }\n }' After the quick-fix is applied: 'void append(StringBuilder sb, int count, Object obj) {\n sb.append(String.valueOf(obj).repeat(Math.max(0, count)));\n }' By default, the inspection may wrap 'count' with 'Math.max(0, count)' if it cannot prove statically that 'count' is not negative. This is done to prevent possible semantics change, as 'String.repeat()' rejects negative numbers. Use the Add Math.max(0,count) to avoid possible semantics change option to disable this behavior if required. Similarly, a string you want to repeat can be wrapped in 'String.valueOf' to prevent possible 'NullPointerException' if it's unknown whether it can be 'null'. This inspection only reports if the language level of the project or module is 11 or higher. New in 2019.1", + "markdown": "Reports loops that can be replaced with a single `String.repeat()` method (available since Java 11).\n\n**Example:**\n\n\n void append(StringBuilder sb, int count, Object obj) {\n for (int i = 0; i < count; i++) {\n sb.append(obj);\n }\n }\n\nAfter the quick-fix is applied:\n\n\n void append(StringBuilder sb, int count, Object obj) {\n sb.append(String.valueOf(obj).repeat(Math.max(0, count)));\n }\n\n\nBy default, the inspection may wrap `count` with `Math.max(0, count)` if it cannot prove statically that `count` is\nnot negative. This is done to prevent possible semantics change, as `String.repeat()` rejects negative numbers.\nUse the **Add Math.max(0,count) to avoid possible semantics change** option to disable this behavior if required.\n\nSimilarly, a string you want to repeat can be wrapped in\n`String.valueOf` to prevent possible `NullPointerException` if it's unknown whether it can be `null`.\n\nThis inspection only reports if the language level of the project or module is 11 or higher.\n\nNew in 2019.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StringRepeatCanBeUsed", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 11", + "index": 121, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnsatisfiedRange", + "shortDescription": { + "text": "Return value is outside of declared range" + }, + "fullDescription": { + "text": "Reports numeric values returned from methods that don't conform to the declared method return range. You can declare method return range using a number of annotations: 'org.jetbrains.annotations.Range' from JetBrains annotations package (specify 'from' and 'to') 'org.checkerframework.common.value.qual.IntRange' from Checker Framework annotations package (specify 'from' and 'to') 'org.checkerframework.checker.index.qual.GTENegativeOne' from Checker Framework annotations package (range is '>= -1') 'org.checkerframework.checker.index.qual.NonNegative' from Checker Framework annotations package (range is '>= 0') 'org.checkerframework.checker.index.qual.Positive' from Checker Framework annotations package (range is '> 0') 'javax.annotation.Nonnegative' from JSR 305 annotations package (range is '>= 0') 'javax.validation.constraints.Min' (specify minimum value) 'javax.validation.constraints.Max' (specify maximum value) Example: '@Range(from = 0, to = Integer.MAX_VALUE) int getValue() {\n // Warning: -1 is outside of declared range\n return -1;\n }' New in 2021.2", + "markdown": "Reports numeric values returned from methods that don't conform to the declared method return range. You can declare method return range using a number of annotations:\n\n* `org.jetbrains.annotations.Range` from JetBrains annotations package (specify 'from' and 'to')\n* `org.checkerframework.common.value.qual.IntRange` from Checker Framework annotations package (specify 'from' and 'to')\n* `org.checkerframework.checker.index.qual.GTENegativeOne` from Checker Framework annotations package (range is '\\>= -1')\n* `org.checkerframework.checker.index.qual.NonNegative` from Checker Framework annotations package (range is '\\>= 0')\n* `org.checkerframework.checker.index.qual.Positive` from Checker Framework annotations package (range is '\\> 0')\n* `javax.annotation.Nonnegative` from JSR 305 annotations package (range is '\\>= 0')\n* `javax.validation.constraints.Min` (specify minimum value)\n* `javax.validation.constraints.Max` (specify maximum value)\n\nExample:\n\n\n @Range(from = 0, to = Integer.MAX_VALUE) int getValue() {\n // Warning: -1 is outside of declared range\n return -1;\n }\n\nNew in 2021.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnsatisfiedRange", + "cweIds": [ + 129, + 682 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs/Nullability problems", + "index": 115, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SystemSetSecurityManager", + "shortDescription": { + "text": "Call to 'System.setSecurityManager()'" + }, + "fullDescription": { + "text": "Reports calls to 'System.setSecurityManager()'. While often benign, any call to 'System.setSecurityManager()' should be closely examined in any security audit.", + "markdown": "Reports calls to `System.setSecurityManager()`.\n\nWhile often benign, any call to `System.setSecurityManager()` should be closely examined in any security audit." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CallToSystemSetSecurityManager", + "cweIds": [ + 250 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Security", + "index": 30, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassWithoutNoArgConstructor", + "shortDescription": { + "text": "Class without no-arg constructor" + }, + "fullDescription": { + "text": "Reports classes without a constructor that takes no arguments (i.e. has no parameters). No-arg constructors are necessary in some contexts. For example, if a class needs to be created using reflection. Example: 'public class Bean {\n private String name;\n\n public Bean(String name) {\n this.name = name;\n }\n }' Use the checkbox below to ignore classes without explicit constructors. The compiler provides a default no-arg constructor to such classes.", + "markdown": "Reports classes without a constructor that takes no arguments (i.e. has no parameters). No-arg constructors are necessary in some contexts. For example, if a class needs to be created using reflection.\n\n**Example:**\n\n\n public class Bean {\n private String name;\n\n public Bean(String name) {\n this.name = name;\n }\n }\n\n\nUse the checkbox below to ignore classes without explicit constructors.\nThe compiler provides a default no-arg constructor to such classes." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassWithoutNoArgConstructor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/JavaBeans issues", + "index": 33, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassWithTooManyDependencies", + "shortDescription": { + "text": "Class with too many dependencies" + }, + "fullDescription": { + "text": "Reports classes that are directly dependent on too many other classes in the project. Modifications to any dependency of such classes may require changing the class, thus making it prone to instability. Only top-level classes are reported. Use the Maximum number of dependencies field to specify the maximum allowed number of dependencies for a class. Available only from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name and isn't reported in the editor.", + "markdown": "Reports classes that are directly dependent on too many other classes in the project.\n\nModifications to any dependency of such classes may require changing the class, thus making it prone to instability.\n\nOnly top-level classes are reported.\n\nUse the **Maximum number of dependencies** field to specify the maximum allowed number of dependencies for a class.\n\nAvailable only from **Code \\| Inspect Code** or\n**Code \\| Analyze Code \\| Run Inspection by Name** and isn't reported in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassWithTooManyDependencies", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Dependency issues", + "index": 89, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CastConflictsWithInstanceof", + "shortDescription": { + "text": "Cast conflicts with 'instanceof'" + }, + "fullDescription": { + "text": "Reports type cast expressions that are preceded by an 'instanceof' check for a different type. Although this might be intended, such a construct is most likely an error, and will result in a 'java.lang.ClassCastException' at runtime. Example: 'class Main {\n int whenCharSequenceCastToNumber(Object o){\n if (o instanceof CharSequence) {\n return ((Number) o).intValue();\n }\n return 0;\n }\n\n int earlyReturnWhenNotCharSequence(Object o){\n if (!(o instanceof CharSequence)) return 0;\n return ((Number)o).intValue();\n }\n }'", + "markdown": "Reports type cast expressions that are preceded by an `instanceof` check for a different type.\n\n\nAlthough this might be intended, such a construct is most likely an error, and will\nresult in a `java.lang.ClassCastException` at runtime.\n\n**Example:**\n\n\n class Main {\n int whenCharSequenceCastToNumber(Object o){\n if (o instanceof CharSequence) {\n return ((Number) o).intValue();\n }\n return 0;\n }\n\n int earlyReturnWhenNotCharSequence(Object o){\n if (!(o instanceof CharSequence)) return 0;\n return ((Number)o).intValue();\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CastConflictsWithInstanceof", + "cweIds": [ + 704 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MathRoundingWithIntArgument", + "shortDescription": { + "text": "Call math rounding with 'int' argument" + }, + "fullDescription": { + "text": "Reports calls to 'round()', 'ceil()', 'floor()', 'rint()' methods for 'Math' and 'StrictMath' with 'int' as the argument. These methods could be called in case the argument is expected to be 'long' or 'double', and it may have unexpected results. The inspection provides a fix that simplify such expressions (except 'round') to cast to 'double'. Example: 'int i = 2;\n double d1 = Math.floor(i);' After the quick-fix is applied: 'int i = 2;\n double d1 = i;' New in 2023.1", + "markdown": "Reports calls to `round()`, `ceil()`, `floor()`, `rint()` methods for `Math` and `StrictMath` with `int` as the argument.\n\nThese methods could be called in case the argument is expected to be `long` or `double`, and it may have unexpected results.\n\nThe inspection provides a fix that simplify such expressions (except `round`) to cast to `double`.\n\n**Example:**\n\n\n int i = 2;\n double d1 = Math.floor(i);\n\nAfter the quick-fix is applied:\n\n\n int i = 2;\n double d1 = i;\n\nNew in 2023.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "MathRoundingWithIntArgument", + "cweIds": [ + 681 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Contract", + "shortDescription": { + "text": "Contract issues" + }, + "fullDescription": { + "text": "Reports issues in method '@Contract' annotations. The types of issues that can be reported are: Errors in contract syntax Contracts that do not conform to the method signature (wrong parameter count) Method implementations that contradict the contract (e.g. return 'true' when the contract says 'false') Example: '// method has no parameters, but contract expects 1\n @Contract(\"_ -> fail\")\n void x() {\n throw new AssertionError();\n }'", + "markdown": "Reports issues in method `@Contract` annotations. The types of issues that can be reported are:\n\n* Errors in contract syntax\n* Contracts that do not conform to the method signature (wrong parameter count)\n* Method implementations that contradict the contract (e.g. return `true` when the contract says `false`)\n\nExample:\n\n\n // method has no parameters, but contract expects 1\n @Contract(\"_ -> fail\")\n void x() {\n throw new AssertionError();\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "Contract", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NewExceptionWithoutArguments", + "shortDescription": { + "text": "Exception constructor called without arguments" + }, + "fullDescription": { + "text": "Reports creation of a exception instance without any arguments specified. When an exception is constructed without any arguments, it contains no information about the problem that occurred, which makes debugging needlessly hard. Example: 'throw new IOException(); // warning: exception without arguments'", + "markdown": "Reports creation of a exception instance without any arguments specified.\n\nWhen an exception is constructed without any arguments, it contains no information about the problem that occurred, which makes\ndebugging needlessly hard.\n\n**Example:**\n\n\n throw new IOException(); // warning: exception without arguments\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NewExceptionWithoutArguments", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Error handling", + "index": 15, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EnhancedSwitchBackwardMigration", + "shortDescription": { + "text": "Enhanced 'switch'" + }, + "fullDescription": { + "text": "Reports enhanced 'switch' statements and expressions. Suggests replacing them with regular 'switch' statements. Example: 'boolean even = switch (condition) {\n case 1, 3, 5, 7, 9 -> false;\n default -> true;\n };' After the quick-fix is applied: 'boolean even;\n switch (condition) {\n case 1:\n case 3:\n case 5:\n case 7:\n case 9:\n even = false;\n break;\n default:\n even = true;\n break;\n}' Enhanced 'switch' appeared in Java 14. This inspection can help to downgrade for backward compatibility with earlier Java versions. New in 2019.1", + "markdown": "Reports enhanced `switch` statements and expressions. Suggests replacing them with regular `switch` statements.\n\n**Example:**\n\n\n boolean even = switch (condition) {\n case 1, 3, 5, 7, 9 -> false;\n default -> true;\n };\n\nAfter the quick-fix is applied:\n\n\n boolean even;\n switch (condition) {\n case 1:\n case 3:\n case 5:\n case 7:\n case 9:\n even = false;\n break;\n default:\n even = true;\n break;\n }\n\n\n*Enhanced* `switch` appeared in Java 14.\nThis inspection can help to downgrade for backward compatibility with earlier Java versions.\n\nNew in 2019.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "EnhancedSwitchBackwardMigration", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 14", + "index": 100, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PackageWithTooManyClasses", + "shortDescription": { + "text": "Package with too many classes" + }, + "fullDescription": { + "text": "Reports packages that contain too many classes. Overly large packages may indicate a lack of design clarity. Available only from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name and isn't reported in the editor. Use the Maximum number of classes field to specify the maximum allowed number of classes in a package.", + "markdown": "Reports packages that contain too many classes.\n\nOverly large packages may indicate a lack of design clarity.\n\nAvailable only from **Code \\| Inspect Code** or\n**Code \\| Analyze Code \\| Run Inspection by Name** and isn't reported in the editor.\n\nUse the **Maximum number of classes** field to specify the maximum allowed number of classes in a package." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PackageWithTooManyClasses", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Packaging issues", + "index": 36, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TryFinallyCanBeTryWithResources", + "shortDescription": { + "text": "'try finally' can be replaced with 'try' with resources" + }, + "fullDescription": { + "text": "Reports 'try'-'finally' statements that can use Java 7 Automatic Resource Management, which is less error-prone. A quick-fix is available to convert a 'try'-'finally' statement into a 'try'-with-resources statement. Example: 'PrintStream printStream = new PrintStream(fileName);\n try {\n printStream.print(true);\n } finally {\n printStream.close();\n }' A quick-fix is provided to pass the cause to a constructor: 'try (PrintStream printStream = new PrintStream(fileName)) {\n printStream.print(true);\n }' This inspection only reports if the language level of the project or module is 7 or higher.", + "markdown": "Reports `try`-`finally` statements that can use Java 7 Automatic Resource Management, which is less error-prone.\n\nA quick-fix is available to convert a `try`-`finally`\nstatement into a `try`-with-resources statement.\n\n**Example:**\n\n\n PrintStream printStream = new PrintStream(fileName);\n try {\n printStream.print(true);\n } finally {\n printStream.close();\n }\n\nA quick-fix is provided to pass the cause to a constructor:\n\n\n try (PrintStream printStream = new PrintStream(fileName)) {\n printStream.print(true);\n }\n\nThis inspection only reports if the language level of the project or module is 7 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "TryFinallyCanBeTryWithResources", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 7", + "index": 112, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SynchronizationOnStaticField", + "shortDescription": { + "text": "Synchronization on 'static' field" + }, + "fullDescription": { + "text": "Reports synchronization on 'static' fields. While not strictly incorrect, synchronization on 'static' fields can lead to bad performance because of contention.", + "markdown": "Reports synchronization on `static` fields. While not strictly incorrect, synchronization on `static` fields can lead to bad performance because of contention." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SynchronizationOnStaticField", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SwitchStatementWithConfusingDeclaration", + "shortDescription": { + "text": "Local variable used and declared in different 'switch' branches" + }, + "fullDescription": { + "text": "Reports local variables declared in one branch of a 'switch' statement and used in another branch. Such declarations can be extremely confusing. Example: 'switch(i) {\n case 2:\n int x = 0;\n break;\n case 3:\n x = 3;\n System.out.println(x);\n break;\n }'", + "markdown": "Reports local variables declared in one branch of a `switch` statement and used in another branch. Such declarations can be extremely confusing.\n\nExample:\n\n\n switch(i) {\n case 2:\n int x = 0;\n break;\n case 3:\n x = 3;\n System.out.println(x);\n break;\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "LocalVariableUsedAndDeclaredInDifferentSwitchBranches", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SamePackageImport", + "shortDescription": { + "text": "Unnecessary import from the same package" + }, + "fullDescription": { + "text": "Reports 'import' statements that refer to the same package as the containing file. Same-package files are always implicitly imported, so such 'import' statements are redundant and confusing. Since IntelliJ IDEA can automatically detect and fix such statements with its Optimize Imports command, this inspection is mostly useful for offline reporting on code bases that you don't intend to change.", + "markdown": "Reports `import` statements that refer to the same package as the containing file.\n\n\nSame-package files are always implicitly imported, so such `import`\nstatements are redundant and confusing.\n\n\nSince IntelliJ IDEA can automatically detect and fix such statements with its **Optimize Imports**\ncommand, this inspection is mostly useful for offline reporting on code bases that you\ndon't intend to change." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SamePackageImport", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Imports", + "index": 22, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ThreadLocalSetWithNull", + "shortDescription": { + "text": "'ThreadLocal.set()' with null as an argument" + }, + "fullDescription": { + "text": "Reports 'java.lang.ThreadLocal.set()' with null as an argument. This call does not free the resources, and it may cause a memory leak. It may happen because: Firstly, 'ThreadLocal.set(null)' finds a map associated with the current Thread. If there is no such a map, it will be created It sets key and value: 'map.set(this, value)', where 'this' refers to instance of 'ThreadLocal' 'java.lang.ThreadLocal.remove()' should be used to free the resources. Example: 'ThreadLocal threadLocal = new ThreadLocal<>();\n threadLocal.set(null);' After the quick-fix is applied: 'threadLocal.remove();' New in 2023.2", + "markdown": "Reports `java.lang.ThreadLocal.set()` with null as an argument.\n\nThis call does not free the resources, and it may cause a memory leak.\nIt may happen because:\n\n* Firstly, `ThreadLocal.set(null)` finds a map associated with the current Thread. If there is no such a map, it will be created\n* It sets key and value: `map.set(this, value)`, where `this` refers to instance of `ThreadLocal`\n\n`java.lang.ThreadLocal.remove()` should be used to free the resources.\n\nExample:\n\n\n ThreadLocal threadLocal = new ThreadLocal<>();\n threadLocal.set(null);\n\nAfter the quick-fix is applied:\n\n\n threadLocal.remove();\n\nNew in 2023.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "note", + "parameters": { + "suppressToolId": "ThreadLocalSetWithNull", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FieldMayBeFinal", + "shortDescription": { + "text": "Field may be 'final'" + }, + "fullDescription": { + "text": "Reports fields that can be safely made 'final'. All 'final' fields have a value and this value does not change, which can make the code easier to reason about. To avoid too expensive analysis, this inspection only reports if the field has a 'private' modifier or it is defined in a local or anonymous class. A field can be 'final' if: It is 'static' and initialized once in its declaration or in one 'static' initializer. It is non-'static' and initialized once in its declaration, in one instance initializer or in every constructor And it is not modified anywhere else. Example: 'public class Person {\n private String name; // can be final\n\n Person(String name) {\n this.name = name;\n }\n\n public String getName() {\n return name;\n }\n }' After the quick-fix is applied: 'public class Person {\n private final String name;\n\n Person(String name) {\n this.name = name;\n }\n\n public String getName() {\n return name;\n }\n }' Use the \"Annotations\" button to modify the list of annotations that assume implicit field write.", + "markdown": "Reports fields that can be safely made `final`. All `final` fields have a value and this value does not change, which can make the code easier to reason about.\n\nTo avoid too expensive analysis, this inspection only reports if the field has a `private` modifier\nor it is defined in a local or anonymous class.\nA field can be `final` if:\n\n* It is `static` and initialized once in its declaration or in one `static` initializer.\n* It is non-`static` and initialized once in its declaration, in one instance initializer or in every constructor\n\nAnd it is not modified anywhere else.\n\n**Example:**\n\n\n public class Person {\n private String name; // can be final\n\n Person(String name) {\n this.name = name;\n }\n\n public String getName() {\n return name;\n }\n }\n\nAfter the quick-fix is applied:\n\n\n public class Person {\n private final String name;\n\n Person(String name) {\n this.name = name;\n }\n\n public String getName() {\n return name;\n }\n }\n\n\nUse the \"Annotations\" button to modify the list of annotations that assume implicit field write." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "FieldMayBeFinal", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MissingJavadoc", + "shortDescription": { + "text": "Missing Javadoc" + }, + "fullDescription": { + "text": "Reports missing Javadoc comments and tags. Example: '/**\n * Missing \"@param\" is reported (if configured).\n */\n public void sample(int param){\n }' The quick-fixes add missing tag or missing Javadoc comment: '/**\n * Missing \"@param\" is reported (if configured).\n * @param param\n */\n public void sample(int param){\n }' Inspection can be configured to ignore deprecated elements or simple accessor methods like 'getField()' or 'setField()'. You can also use options below to configure required tags and minimal required visibility for the specific code elements like method, field, class, package, module.", + "markdown": "Reports missing Javadoc comments and tags.\n\nExample:\n\n\n /**\n * Missing \"@param\" is reported (if configured).\n */\n public void sample(int param){\n }\n\nThe quick-fixes add missing tag or missing Javadoc comment:\n\n\n /**\n * Missing \"@param\" is reported (if configured).\n * @param param\n */\n public void sample(int param){\n }\n\n\nInspection can be configured to ignore deprecated elements or simple accessor methods like `getField()` or `setField()`.\nYou can also use options below to configure required tags and minimal required visibility for the specific code elements like method, field, class, package, module." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MissingJavadoc", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Javadoc", + "index": 45, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspiciousArrayMethodCall", + "shortDescription": { + "text": "Suspicious 'Arrays' method call" + }, + "fullDescription": { + "text": "Reports calls to non-generic-array manipulation methods like 'Arrays.fill()' with mismatched argument types. Such calls don't do anything useful and are likely to be mistakes. Example: 'int foo(String[] strings) {\n return Arrays.binarySearch(strings, 1);\n }' New in 2017.2", + "markdown": "Reports calls to non-generic-array manipulation methods like `Arrays.fill()` with mismatched argument types. Such calls don't do anything useful and are likely to be mistakes.\n\n**Example:**\n\n\n int foo(String[] strings) {\n return Arrays.binarySearch(strings, 1);\n }\n\nNew in 2017.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SuspiciousArrayMethodCall", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassHasNoToStringMethod", + "shortDescription": { + "text": "Class does not override 'toString()' method" + }, + "fullDescription": { + "text": "Reports classes without a 'toString()' method.", + "markdown": "Reports classes without a `toString()` method." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassHasNoToStringMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/toString() issues", + "index": 118, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AwaitWithoutCorrespondingSignal", + "shortDescription": { + "text": "'await()' without corresponding 'signal()'" + }, + "fullDescription": { + "text": "Reports calls to 'Condition.await()', for which no call to a corresponding 'Condition.signal()' or 'Condition.signalAll()' can be found. Calling 'Condition.await()' in a thread without corresponding 'Condition.signal()' may cause the thread to become disabled until it is interrupted or \"spurious wakeup\" occurs. Only calls that target fields of the current class are reported by this inspection. Example: 'class Queue {\n private final Condition isEmpty = ...;\n\n void add(Object elem) {\n // ...\n // isEmpty.signal();\n // ...\n }\n\n void remove(Object elem) throws InterruptedException {\n // ...\n isEmpty.await(); // 'await()' doesn't contain corresponding 'signal()'/'signalAll()' call\n // ...\n }\n }'", + "markdown": "Reports calls to `Condition.await()`, for which no call to a corresponding `Condition.signal()` or `Condition.signalAll()` can be found.\n\n\nCalling `Condition.await()` in a thread without corresponding `Condition.signal()` may cause the thread\nto become disabled until it is interrupted or \"spurious wakeup\" occurs.\n\nOnly calls that target fields of the current class are reported by this inspection.\n\n**Example:**\n\n\n class Queue {\n private final Condition isEmpty = ...;\n\n void add(Object elem) {\n // ...\n // isEmpty.signal();\n // ...\n }\n\n void remove(Object elem) throws InterruptedException {\n // ...\n isEmpty.await(); // 'await()' doesn't contain corresponding 'signal()'/'signalAll()' call\n // ...\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AwaitWithoutCorrespondingSignal", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Java9RedundantRequiresStatement", + "shortDescription": { + "text": "Redundant 'requires' directive in module-info" + }, + "fullDescription": { + "text": "Reports redundant 'requires' directives in Java Platform Module System 'module-info.java' files. A 'requires' directive is redundant when a module 'A' requires a module 'B', but the code in module 'A' doesn't import any packages or classes from 'B'. Furthermore, all modules have an implicitly declared dependence on the 'java.base' module, therefore a 'requires java.base;' directive is always redundant. The quick-fix deletes the redundant 'requires' directive. If the deleted dependency re-exported modules that are actually used, the fix adds a 'requires' directives for these modules. This inspection only reports if the language level of the project or module is 9 or higher. New in 2017.1", + "markdown": "Reports redundant `requires` directives in Java Platform Module System `module-info.java` files. A `requires` directive is redundant when a module `A` requires a module `B`, but the code in module `A` doesn't import any packages or classes from `B`. Furthermore, all modules have an implicitly declared dependence on the `java.base` module, therefore a `requires java.base;` directive is always redundant.\n\n\nThe quick-fix deletes the redundant `requires` directive.\nIf the deleted dependency re-exported modules that are actually used, the fix adds a `requires` directives for these modules.\n\nThis inspection only reports if the language level of the project or module is 9 or higher.\n\nNew in 2017.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "Java9RedundantRequiresStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryLocalVariable", + "shortDescription": { + "text": "Redundant local variable" + }, + "fullDescription": { + "text": "Reports unnecessary local variables that add nothing to the comprehensibility of a method, including: Local variables that are immediately returned. Local variables that are immediately assigned to another variable and then not used. Local variables that always have the same value as another local variable or parameter. Example: 'boolean yes() {\n boolean b = true;\n return b;\n }' After the quick-fix is applied: 'boolean yes() {\n return true;\n }' Configure the inspection: Use the Ignore immediately returned or thrown variables option to ignore immediately returned or thrown variables. Some coding styles suggest using such variables for clarity and ease of debugging. Use the Ignore variables which have an annotation option to ignore annotated variables.", + "markdown": "Reports unnecessary local variables that add nothing to the comprehensibility of a method, including:\n\n* Local variables that are immediately returned.\n* Local variables that are immediately assigned to another variable and then not used.\n* Local variables that always have the same value as another local variable or parameter.\n\n**Example:**\n\n\n boolean yes() {\n boolean b = true;\n return b;\n }\n\nAfter the quick-fix is applied:\n\n\n boolean yes() {\n return true;\n }\n \nConfigure the inspection:\n\n* Use the **Ignore immediately returned or thrown variables** option to ignore immediately returned or thrown variables. Some coding styles suggest using such variables for clarity and ease of debugging.\n* Use the **Ignore variables which have an annotation** option to ignore annotated variables." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryLocalVariable", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Data flow", + "index": 23, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FieldAccessNotGuarded", + "shortDescription": { + "text": "Unguarded field access or method call" + }, + "fullDescription": { + "text": "Reports accesses of fields declared as '@GuardedBy' that are not guarded by an appropriate synchronization structure. Example: '@GuardedBy(\"this\")\n void x() {\n notify();\n }\n void y() {\n x(); // unguarded method call\n }' Supported '@GuardedBy' annotations are: 'net.jcip.annotations.GuardedBy' 'javax.annotation.concurrent.GuardedBy' 'org.apache.http.annotation.GuardedBy' 'com.android.annotations.concurrency.GuardedBy' 'androidx.annotation.GuardedBy' 'com.google.errorprone.annotations.concurrent.GuardedBy'", + "markdown": "Reports accesses of fields declared as `@GuardedBy` that are not guarded by an appropriate synchronization structure.\n\nExample:\n\n\n @GuardedBy(\"this\")\n void x() {\n notify();\n }\n void y() {\n x(); // unguarded method call\n }\n\nSupported `@GuardedBy` annotations are:\n\n* `net.jcip.annotations.GuardedBy`\n* `javax.annotation.concurrent.GuardedBy`\n* `org.apache.http.annotation.GuardedBy`\n* `com.android.annotations.concurrency.GuardedBy`\n* `androidx.annotation.GuardedBy`\n* `com.google.errorprone.annotations.concurrent.GuardedBy`" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "FieldAccessNotGuarded", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Concurrency annotation issues", + "index": 61, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BusyWait", + "shortDescription": { + "text": "Busy wait" + }, + "fullDescription": { + "text": "Reports calls to 'java.lang.Thread.sleep()' that occur inside loops. Such calls are indicative of \"busy-waiting\". Busy-waiting is often inefficient, and may result in unexpected deadlocks as busy-waiting threads do not release locked resources. Example: 'class X {\n volatile int x;\n public void waitX() throws Exception {\n while (x > 0) {\n Thread.sleep(10);//warning: Call to 'Thread.sleep()' in a loop, probably busy-waiting\n }\n }\n }'", + "markdown": "Reports calls to `java.lang.Thread.sleep()` that occur inside loops.\n\nSuch calls\nare indicative of \"busy-waiting\". Busy-waiting is often inefficient, and may result in unexpected deadlocks\nas busy-waiting threads do not release locked resources.\n\n**Example:**\n\n\n class X {\n volatile int x;\n public void waitX() throws Exception {\n while (x > 0) {\n Thread.sleep(10);//warning: Call to 'Thread.sleep()' in a loop, probably busy-waiting\n }\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "BusyWait", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ForLoopWithMissingComponent", + "shortDescription": { + "text": "'for' loop with missing components" + }, + "fullDescription": { + "text": "Reports 'for' loops that lack initialization, condition, or update clauses. Some coding styles prohibit such loops. Example: 'for (int i = 0;;i++) {\n // body\n }' Use the Ignore collection iterations option to ignore loops which use an iterator. This is a standard way to iterate over a collection in which the 'for' loop does not have an update clause.", + "markdown": "Reports `for` loops that lack initialization, condition, or update clauses. Some coding styles prohibit such loops.\n\nExample:\n\n\n for (int i = 0;;i++) {\n // body\n }\n\n\nUse the **Ignore collection iterations** option to ignore loops which use an iterator.\nThis is a standard way to iterate over a collection in which the `for` loop does not have an update clause." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ForLoopWithMissingComponent", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PublicStaticArrayField", + "shortDescription": { + "text": "'public static' array field" + }, + "fullDescription": { + "text": "Reports 'public' 'static' array fields. Such fields are often used to store arrays of constant values. Still, they represent a security hazard, as their contents may be modified, even if the field is declared 'final'. Example: 'public static String[] allowedPasswords = {\"foo\", \"bar\"};'", + "markdown": "Reports `public` `static` array fields.\n\n\nSuch fields are often used to store arrays of constant values. Still, they represent a security\nhazard, as their contents may be modified, even if the field is declared `final`.\n\n**Example:**\n\n\n public static String[] allowedPasswords = {\"foo\", \"bar\"};\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PublicStaticArrayField", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Security", + "index": 30, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MagicNumber", + "shortDescription": { + "text": "Magic number" + }, + "fullDescription": { + "text": "Reports \"magic numbers\": numeric literals that are not named by a constant declaration. Using magic numbers can lead to unclear code, as well as errors if a magic number is changed in one location but remains unchanged not another. The numbers 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1000, 0L, 1L, 2L, 0.0, 1.0, 0.0F and 1.0F are not reported by this inspection. Example: 'void checkFileSize(long bytes) {\n if (bytes > 1_048_576) {\n throw new IllegalArgumentException(\"too big\");\n }\n }' A quick-fix introduces a new constant: 'static final int MAX_SUPPORTED_FILE_SIZE = 1_048_576;\n\n void checkFileSize(long bytes) {\n if (bytes > MAX_SUPPORTED_FILE_SIZE) {\n throw new IllegalArgumentException(\"too big\");\n }\n }' Configure the inspection: Use the Ignore constants in 'hashCode()' methods option to disable this inspection within 'hashCode()' methods. Use the Ignore in annotations option to ignore magic numbers in annotations. Use the Ignore initial capacity for StringBuilders and Collections option to ignore magic numbers used as initial capacity when constructing 'Collection', 'Map', 'StringBuilder' or 'StringBuffer' objects.", + "markdown": "Reports \"magic numbers\": numeric literals that are not named by a constant declaration.\n\nUsing magic numbers can lead to unclear code, as well as errors if a magic\nnumber is changed in one location but remains unchanged not another. The numbers 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 1000, 0L, 1L, 2L,\n0.0, 1.0, 0.0F and 1.0F are not reported by this inspection.\n\nExample:\n\n\n void checkFileSize(long bytes) {\n if (bytes > 1_048_576) {\n throw new IllegalArgumentException(\"too big\");\n }\n }\n\nA quick-fix introduces a new constant:\n\n\n static final int MAX_SUPPORTED_FILE_SIZE = 1_048_576;\n\n void checkFileSize(long bytes) {\n if (bytes > MAX_SUPPORTED_FILE_SIZE) {\n throw new IllegalArgumentException(\"too big\");\n }\n }\n\nConfigure the inspection:\n\n* Use the **Ignore constants in 'hashCode()' methods** option to disable this inspection within `hashCode()` methods.\n* Use the **Ignore in annotations** option to ignore magic numbers in annotations.\n* Use the **Ignore initial capacity for StringBuilders and Collections** option to ignore magic numbers used as initial capacity when constructing `Collection`, `Map`, `StringBuilder` or `StringBuffer` objects." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MagicNumber", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Abstraction issues", + "index": 78, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OverwrittenKey", + "shortDescription": { + "text": "Overwritten Map, Set, or array element" + }, + "fullDescription": { + "text": "Reports code that overwrites a 'Map' key, a 'Set' element, or an array element in a sequence of 'add'/'put' calls or using a Java 9 factory method like 'Set.of' (which will result in runtime exception). This usually occurs due to a copy-paste error. Example: 'map.put(\"A\", 1);\n map.put(\"B\", 2);\n map.put(\"C\", 3);\n map.put(\"D\", 4);\n map.put(\"A\", 5); // duplicating key \"A\", overwrites the previously written entry' New in 2017.3", + "markdown": "Reports code that overwrites a `Map` key, a `Set` element, or an array element in a sequence of `add`/`put` calls or using a Java 9 factory method like `Set.of` (which will result in runtime exception).\n\nThis usually occurs due to a copy-paste error.\n\n**Example:**\n\n\n map.put(\"A\", 1);\n map.put(\"B\", 2);\n map.put(\"C\", 3);\n map.put(\"D\", 4);\n map.put(\"A\", 5); // duplicating key \"A\", overwrites the previously written entry\n\nNew in 2017.3" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "OverwrittenKey", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AnonymousClassMethodCount", + "shortDescription": { + "text": "Anonymous inner class with too many methods" + }, + "fullDescription": { + "text": "Reports anonymous inner classes whose method count exceeds the specified maximum. Anonymous classes with numerous methods may be difficult to understand and should be promoted to become named inner classes. Use the Method count limit field to specify the maximum allowed number of methods in an anonymous inner class.", + "markdown": "Reports anonymous inner classes whose method count exceeds the specified maximum.\n\nAnonymous classes with numerous methods may be\ndifficult to understand and should be promoted to become named inner classes.\n\nUse the **Method count limit** field to specify the maximum allowed number of methods in an anonymous inner class." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AnonymousInnerClassWithTooManyMethods", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class metrics", + "index": 87, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EnumSwitchStatementWhichMissesCases", + "shortDescription": { + "text": "Enum 'switch' statement that misses case" + }, + "fullDescription": { + "text": "Reports 'switch' statements over enumerated types that are not exhaustive. Example: 'enum AlphaBetaGamma {\n A, B, C;\n\n void x(AlphaBetaGamma e) {\n switch (e) {\n\n }\n }\n }' After the quick-fix is applied: 'enum AlphaBetaGamma {\n A, B, C;\n\n void x(AlphaBetaGamma e) {\n switch (e) {\n case A -> {}\n case B -> {}\n case C -> {}\n }\n }\n }' Use the Ignore switch statements with a default branch option to ignore 'switch' statements that have a 'default' branch.", + "markdown": "Reports `switch` statements over enumerated types that are not exhaustive.\n\n**Example:**\n\n\n enum AlphaBetaGamma {\n A, B, C;\n\n void x(AlphaBetaGamma e) {\n switch (e) {\n\n }\n }\n }\n\nAfter the quick-fix is applied:\n\n\n enum AlphaBetaGamma {\n A, B, C;\n\n void x(AlphaBetaGamma e) {\n switch (e) {\n case A -> {}\n case B -> {}\n case C -> {}\n }\n }\n }\n\n\nUse the **Ignore switch statements with a default branch** option to ignore `switch`\nstatements that have a `default` branch." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "EnumSwitchStatementWhichMissesCases", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspiciousIntegerDivAssignment", + "shortDescription": { + "text": "Suspicious integer division assignment" + }, + "fullDescription": { + "text": "Reports assignments whose right side is a division that shouldn't be truncated to integer. While occasionally intended, this construction is often buggy. Example: 'int x = 18;\n x *= 3/2; // doesn't change x because of the integer division result' This code should be replaced with: 'int x = 18;\n x *= 3.0/2;' In the inspection options, you can disable warnings for suspicious but possibly correct divisions, for example, when the dividend can't be calculated statically. 'void calc(int d) {\n int x = 18;\n x *= d/2;\n }' New in 2019.2", + "markdown": "Reports assignments whose right side is a division that shouldn't be truncated to integer.\n\nWhile occasionally intended, this construction is often buggy.\n\n**Example:**\n\n\n int x = 18;\n x *= 3/2; // doesn't change x because of the integer division result\n\n\nThis code should be replaced with:\n\n\n int x = 18;\n x *= 3.0/2;\n\n\nIn the inspection options, you can disable warnings for suspicious but possibly correct divisions,\nfor example, when the dividend can't be calculated statically.\n\n\n void calc(int d) {\n int x = 18;\n x *= d/2;\n }\n\n\nNew in 2019.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SuspiciousIntegerDivAssignment", + "cweIds": [ + 682 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NegativelyNamedBooleanVariable", + "shortDescription": { + "text": "Negatively named boolean variable" + }, + "fullDescription": { + "text": "Reports negatively named variables, for example: 'disabled', 'hidden', or 'isNotChanged'. Usually, inverting the 'boolean' value and removing the negation from the name makes the code easier to understand. Example: 'boolean disabled = false;'", + "markdown": "Reports negatively named variables, for example: `disabled`, `hidden`, or `isNotChanged`.\n\nUsually, inverting the `boolean` value and removing the negation from the name makes the code easier to understand.\n\nExample:\n\n\n boolean disabled = false;\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NegativelyNamedBooleanVariable", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Data flow", + "index": 23, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MethodWithMultipleLoops", + "shortDescription": { + "text": "Method with multiple loops" + }, + "fullDescription": { + "text": "Reports methods that contain more than one loop statement. Example: The method below will be reported because it contains two loops: 'void methodWithTwoLoops(int n1, int n2) {\n for (int i = 0; i < n1; i++) {\n System.out.println(i);\n }\n\n int j = 0;\n while (j < n2) {\n System.out.println(j);\n j++;\n }\n }' The following method will also be reported because it contains a nested loop: 'void methodWithNestedLoop(int n1, int n2) {\n for (int i = 0; i < n1; i++) {\n for (int j = 0; j < n2; j++) {\n System.out.println(i + j);\n }\n }\n }'", + "markdown": "Reports methods that contain more than one loop statement.\n\n**Example:**\n\nThe method below will be reported because it contains two loops:\n\n\n void methodWithTwoLoops(int n1, int n2) {\n for (int i = 0; i < n1; i++) {\n System.out.println(i);\n }\n\n int j = 0;\n while (j < n2) {\n System.out.println(j);\n j++;\n }\n }\n\nThe following method will also be reported because it contains a nested loop:\n\n\n void methodWithNestedLoop(int n1, int n2) {\n for (int i = 0; i < n1; i++) {\n for (int j = 0; j < n2; j++) {\n System.out.println(i + j);\n }\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MethodWithMultipleLoops", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Method metrics", + "index": 95, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SleepWhileHoldingLock", + "shortDescription": { + "text": "Call to 'Thread.sleep()' while synchronized" + }, + "fullDescription": { + "text": "Reports calls to 'java.lang.Thread.sleep()' methods that occur within a 'synchronized' block or method. 'sleep()' within a 'synchronized' block may result in decreased performance, poor scalability, and possibly even deadlocking. Consider using 'wait()' instead, as it will release the lock held. Example: 'synchronized (lock) {\n Thread.sleep(100);\n }'", + "markdown": "Reports calls to `java.lang.Thread.sleep()` methods that occur within a `synchronized` block or method.\n\n\n`sleep()` within a\n`synchronized` block may result in decreased performance, poor scalability, and possibly\neven deadlocking. Consider using `wait()` instead,\nas it will release the lock held.\n\n**Example:**\n\n\n synchronized (lock) {\n Thread.sleep(100);\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SleepWhileHoldingLock", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Threading issues", + "index": 8, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DeprecatedClassUsageInspection", + "shortDescription": { + "text": "Deprecated API usage in XML" + }, + "fullDescription": { + "text": "Reports usages of deprecated classes and methods in XML files.", + "markdown": "Reports usages of deprecated classes and methods in XML files." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "DeprecatedClassUsageInspection", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "XML", + "index": 65, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OptionalOfNullableMisuse", + "shortDescription": { + "text": "Use of Optional.ofNullable with null or not-null argument" + }, + "fullDescription": { + "text": "Reports uses of 'Optional.ofNullable()' where always null or always not-null argument is passed. There's no point in using 'Optional.ofNullable()' in this case: either 'Optional.empty()' or 'Optional.of()' should be used to explicitly state the intent of creating an always-empty or always non-empty optional respectively. It's also possible that there's a mistake in 'Optional.ofNullable()' argument, so it should be examined. Example: 'Optional empty = Optional.ofNullable(null); // should be Optional.empty();\nOptional present = Optional.ofNullable(\"value\"); // should be Optional.of(\"value\");'", + "markdown": "Reports uses of `Optional.ofNullable()` where always null or always not-null argument is passed. There's no point in using `Optional.ofNullable()` in this case: either `Optional.empty()` or `Optional.of()` should be used to explicitly state the intent of creating an always-empty or always non-empty optional respectively. It's also possible that there's a mistake in `Optional.ofNullable()` argument, so it should be examined.\n\n\nExample:\n\n\n Optional empty = Optional.ofNullable(null); // should be Optional.empty();\n Optional present = Optional.ofNullable(\"value\"); // should be Optional.of(\"value\"); \n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "OptionalOfNullableMisuse", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CastToIncompatibleInterface", + "shortDescription": { + "text": "Cast to incompatible type" + }, + "fullDescription": { + "text": "Reports type cast expressions where the casted expression has a class/interface type that neither extends/implements the cast class/interface type, nor has subclasses that do. Such a construct is likely erroneous, and will throw a 'java.lang.ClassCastException' at runtime. Example: 'interface A {}\n interface Z {}\n static class C {}\n\n void x(C c) {\n if (c instanceof Z) {\n A a = ((A)c); // cast to incompatible interface 'A'\n }\n }'", + "markdown": "Reports type cast expressions where the casted expression has a class/interface type that neither extends/implements the cast class/interface type, nor has subclasses that do.\n\n\nSuch a construct is likely erroneous, and will\nthrow a `java.lang.ClassCastException` at runtime.\n\n**Example:**\n\n\n interface A {}\n interface Z {}\n static class C {}\n\n void x(C c) {\n if (c instanceof Z) {\n A a = ((A)c); // cast to incompatible interface 'A'\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CastToIncompatibleInterface", + "cweIds": [ + 704 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MethodReturnAlwaysConstant", + "shortDescription": { + "text": "Method returns per-class constant" + }, + "fullDescription": { + "text": "Reports methods that only return a constant, which may differ for various inheritors. Available only from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name and isn't reported in the editor.", + "markdown": "Reports methods that only return a constant, which may differ for various inheritors.\n\nAvailable only from **Code \\| Inspect Code** or\n**Code \\| Analyze Code \\| Run Inspection by Name** and isn't reported in the editor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MethodReturnAlwaysConstant", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DuplicateBranchesInSwitch", + "shortDescription": { + "text": "Duplicate branches in 'switch'" + }, + "fullDescription": { + "text": "Reports 'switch' statements or expressions that contain the same code in different branches and suggests merging the duplicate branches. Example: 'switch (n) {\n case 1:\n System.out.println(n);\n break;\n case 2:\n System.out.println(n);\n break;\n default:\n System.out.println(\"default\");\n }' After the quick-fix is applied: 'switch (n) {\n case 1:\n case 2:\n System.out.println(n);\n break;\n default:\n System.out.println(\"default\");\n }' New in 2019.1", + "markdown": "Reports `switch` statements or expressions that contain the same code in different branches and suggests merging the duplicate branches.\n\nExample:\n\n\n switch (n) {\n case 1:\n System.out.println(n);\n break;\n case 2:\n System.out.println(n);\n break;\n default:\n System.out.println(\"default\");\n }\n\nAfter the quick-fix is applied:\n\n\n switch (n) {\n case 1:\n case 2:\n System.out.println(n);\n break;\n default:\n System.out.println(\"default\");\n }\n\nNew in 2019.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "DuplicateBranchesInSwitch", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SingleElementAnnotation", + "shortDescription": { + "text": "Non-normalized annotation" + }, + "fullDescription": { + "text": "Reports annotations in a shorthand form and suggests rewriting them in a normal form with an attribute name. Example: '@SuppressWarnings(\"foo\")' After the quick-fix is applied: '@SuppressWarnings(value = \"foo\")'", + "markdown": "Reports annotations in a shorthand form and suggests rewriting them in a normal form with an attribute name.\n\nExample:\n\n\n @SuppressWarnings(\"foo\")\n\nAfter the quick-fix is applied:\n\n\n @SuppressWarnings(value = \"foo\")\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "SingleElementAnnotation", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SimplifiableConditionalExpression", + "shortDescription": { + "text": "Simplifiable conditional expression" + }, + "fullDescription": { + "text": "Reports conditional expressions and suggests simplifying them. Examples: 'condition ? true : foo → condition || foo' 'condition ? false : foo → !condition && foo' 'condition ? foo : !foo → condition == foo' 'condition ? true : false → condition' 'a == b ? b : a → a' 'result != null ? result : null → result'", + "markdown": "Reports conditional expressions and suggests simplifying them.\n\nExamples:\n\n condition ? true : foo → condition || foo\n\n condition ? false : foo → !condition && foo\n\n condition ? foo : !foo → condition == foo\n\n condition ? true : false → condition\n\n a == b ? b : a → a\n\n result != null ? result : null → result\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SimplifiableConditionalExpression", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ManualMinMaxCalculation", + "shortDescription": { + "text": "Manual min/max calculation" + }, + "fullDescription": { + "text": "Reports cases where the minimum or the maximum of two numbers can be calculated using a 'Math.max()' or 'Math.min()' call, instead of doing it manually. Example: 'public int min(int a, int b) {\n return b < a ? b : a;\n }' After the quick-fix is applied: 'public int min(int a, int b) {\n return Math.min(a, b);\n }' Use the Disable for float and double option to disable this inspection for 'double' and 'float' types. This is useful because the quick-fix may slightly change the semantics for 'float'/ 'double' types when handling 'NaN'. Nevertheless, in most cases this will actually fix a subtle bug where 'NaN' is not taken into account. New in 2019.2", + "markdown": "Reports cases where the minimum or the maximum of two numbers can be calculated using a `Math.max()` or `Math.min()` call, instead of doing it manually.\n\n**Example:**\n\n\n public int min(int a, int b) {\n return b < a ? b : a;\n }\n\nAfter the quick-fix is applied:\n\n\n public int min(int a, int b) {\n return Math.min(a, b);\n }\n\n\nUse the **Disable for float and double** option to disable this inspection for `double` and `float` types.\nThis is useful because the quick-fix may slightly change the semantics for `float`/\n`double` types when handling `NaN`. Nevertheless, in most cases this will actually fix\na subtle bug where `NaN` is not taken into account.\n\nNew in 2019.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ManualMinMaxCalculation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SillyAssignment", + "shortDescription": { + "text": "Variable is assigned to itself" + }, + "fullDescription": { + "text": "Reports assignments of a variable to itself. Example: 'a = a;' The quick-fix removes the assigment.", + "markdown": "Reports assignments of a variable to itself.\n\n**Example:**\n\n\n a = a;\n\nThe quick-fix removes the assigment." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SillyAssignment", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BoundedWildcard", + "shortDescription": { + "text": "Can use bounded wildcard" + }, + "fullDescription": { + "text": "Reports generic method parameters that can make use of bounded wildcards. Example: 'void process(Consumer consumer);' should be replaced with: 'void process(Consumer consumer);' This method signature is more flexible because it accepts more types: not only 'Consumer', but also 'Consumer'. Likewise, type parameters in covariant position: 'T produce(Producer p);' should be replaced with: 'T produce(Producer p);' To quote Joshua Bloch in Effective Java third Edition: Item 31: Use bounded wildcards to increase API flexibility Using wildcard types in your APIs, while tricky, makes the APIs far more flexible. If you write a library that will be widely used, the proper use of wildcard types should be considered mandatory. Remember the basic rule: producer-extends, consumer-super (PECS). Also remember that all Comparables and Comparators are consumers. Use the inspection options to toggle the reporting for: invariant classes. An example of an invariant class is 'java.util.List' because it both accepts values (via the 'List.add(T)' method) and produces values (via the 'T List.get()' method). On the other hand, 'contravariant' classes only receive values, for example, 'java.util.function.Consumer' with the only method 'accept(T)'. Similarly, 'covariant' classes only produce values, for example, 'java.util.function.Supplier' with the only method 'T get()'. People often use bounded wildcards in covariant/contravariant classes but avoid wildcards in invariant classes, for example, 'void process(List l)'. Disable this option to ignore such invariant classes and leave them rigidly typed, for example, 'void process(List l)'. 'private' methods, which can be considered as not a part of the public API instance methods", + "markdown": "Reports generic method parameters that can make use of [bounded wildcards](https://en.wikipedia.org/wiki/Wildcard_(Java)).\n\n**Example:**\n\n\n void process(Consumer consumer);\n\nshould be replaced with:\n\n\n void process(Consumer consumer);\n\n\nThis method signature is more flexible because it accepts more types: not only\n`Consumer`, but also `Consumer`.\n\nLikewise, type parameters in covariant position:\n\n\n T produce(Producer p);\n\nshould be replaced with:\n\n\n T produce(Producer p);\n\n\nTo quote [Joshua Bloch](https://en.wikipedia.org/wiki/Joshua_Bloch#Effective_Java) in *Effective Java* third Edition:\n>\n> #### Item 31: Use bounded wildcards to increase API flexibility\n>\n> Using wildcard types in your APIs, while tricky, makes the APIs far more flexible. If you write a library that will be widely used, the proper use of wildcard types should be considered mandatory. Remember the basic rule: producer-extends, consumer-super (PECS). Also remember that all Comparables and Comparators are consumers.\n\n\nUse the inspection options to toggle the reporting for:\n\n*\n invariant classes. An example of an invariant class is `java.util.List` because it both accepts values\n (via the `List.add(T)` method)\n and produces values (via the `T List.get()` method).\n\n\n On the\n other hand, `contravariant` classes only receive values, for example, `java.util.function.Consumer`\n with the only method `accept(T)`. Similarly, `covariant` classes\n only produce values, for example, `java.util.function.Supplier`\n with the only method `T get()`.\n\n\n People often use bounded wildcards in covariant/contravariant\n classes but avoid wildcards in invariant classes, for example, `void process(List l)`.\n Disable this option to ignore such invariant classes and leave them rigidly typed, for example, `void\n process(List l)`.\n*\n `private` methods, which can be considered as not a part of the public API\n\n*\n instance methods" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "BoundedWildcard", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InstanceofIncompatibleInterface", + "shortDescription": { + "text": "'instanceof' with incompatible type" + }, + "fullDescription": { + "text": "Reports 'instanceof' expressions where the expression that is checked has a class/interface type that neither extends/implements the class/interface type on the right-side of the 'instanceof' expression, nor has subclasses that do. Although it could be intended for e.g. library code, such a construct is likely erroneous, because no instance of any class declared in the project could pass this 'instanceof' test. Example: 'class Foo { }\n\n interface Bar { }\n \n class Main {\n void test(Foo f, Bar b) {\n if (f instanceof Bar) { // problem\n System.out.println(\"fail\");\n }\n if (b instanceof Foo) { // problem\n System.out.println(\"fail\");\n }\n }\n }'", + "markdown": "Reports `instanceof` expressions where the expression that is checked has a class/interface type that neither extends/implements the class/interface type on the right-side of the `instanceof` expression, nor has subclasses that do.\n\n\nAlthough it could be intended for e.g. library code, such a construct is likely erroneous,\nbecause no instance of any class declared in the project could pass this `instanceof` test.\n\n**Example:**\n\n\n class Foo { }\n\n interface Bar { }\n \n class Main {\n void test(Foo f, Bar b) {\n if (f instanceof Bar) { // problem\n System.out.println(\"fail\");\n }\n if (b instanceof Foo) { // problem\n System.out.println(\"fail\");\n }\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "InstanceofIncompatibleInterface", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnqualifiedMethodAccess", + "shortDescription": { + "text": "Instance method call not qualified with 'this'" + }, + "fullDescription": { + "text": "Reports calls to non-'static' methods on the same instance that are not qualified with 'this'. Example: 'class Foo {\n void bar() {}\n\n void foo() {\n bar();\n }\n }' After the quick-fix is applied: 'class Foo {\n void bar() {}\n\n void foo() {\n this.bar();\n }\n }'", + "markdown": "Reports calls to non-`static` methods on the same instance that are not qualified with `this`.\n\n**Example:**\n\n\n class Foo {\n void bar() {}\n\n void foo() {\n bar();\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo {\n void bar() {}\n\n void foo() {\n this.bar();\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnqualifiedMethodAccess", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FunctionalExpressionCanBeFolded", + "shortDescription": { + "text": "Functional expression can be folded" + }, + "fullDescription": { + "text": "Reports method references or lambda expressions that point to a method of their own functional interface type and hence can be replaced with their qualifiers removing unnecessary object allocation. Example: 'SwingUtilities.invokeLater(r::run);\n SwingUtilities.invokeAndWait(() -> r.run());' After the quick-fix is applied: 'SwingUtilities.invokeLater(r);\n SwingUtilities.invokeAndWait(r);' This inspection reports only if the language level of the project or module is 8 or higher.", + "markdown": "Reports method references or lambda expressions that point to a method of their own functional interface type and hence can be replaced with their qualifiers removing unnecessary object allocation.\n\nExample:\n\n\n SwingUtilities.invokeLater(r::run);\n SwingUtilities.invokeAndWait(() -> r.run());\n\nAfter the quick-fix is applied:\n\n\n SwingUtilities.invokeLater(r);\n SwingUtilities.invokeAndWait(r);\n\nThis inspection reports only if the language level of the project or module is 8 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "FunctionalExpressionCanBeFolded", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Declaration redundancy", + "index": 14, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MigrateAssertToMatcherAssert", + "shortDescription": { + "text": "JUnit assertion can be 'assertThat()' call" + }, + "fullDescription": { + "text": "Reports calls to 'Assert.assertEquals()', 'Assert.assertTrue()', etc. methods which can be migrated to Hamcrest declarative style 'Assert.assertThat()' calls. For example: 'public class SubstantialTest {\n @Test\n public void testContents(Collection c, String s) {\n Assert.assertTrue(c.contains(s));\n Assert.assertEquals(c, s);\n Assert.assertNotNull(c);\n Assert.assertNull(c);\n Assert.assertFalse(c.contains(s));\n }\n }' A quick-fix is provided to perform the migration: 'public class SubstantialTest {\n @Test\n public void testContents(Collection c, String s) {\n assertThat(c, hasItem(o));\n assertThat(o, is(c));\n assertThat(c, notNullValue());\n assertThat(c, nullValue());\n assertThat(c, not(hasItem(o)));\n }\n }' This inspection requires that the Hamcrest library is available on the classpath. Use the Statically import matcher's methods option to specify if you want the quick-fix to statically import the Hamcrest matcher methods.", + "markdown": "Reports calls to `Assert.assertEquals()`, `Assert.assertTrue()`, etc. methods which can be migrated to Hamcrest declarative style `Assert.assertThat()` calls.\n\nFor example:\n\n\n public class SubstantialTest {\n @Test\n public void testContents(Collection c, String s) {\n Assert.assertTrue(c.contains(s));\n Assert.assertEquals(c, s);\n Assert.assertNotNull(c);\n Assert.assertNull(c);\n Assert.assertFalse(c.contains(s));\n }\n }\n\nA quick-fix is provided to perform the migration:\n\n\n public class SubstantialTest {\n @Test\n public void testContents(Collection c, String s) {\n assertThat(c, hasItem(o));\n assertThat(o, is(c));\n assertThat(c, notNullValue());\n assertThat(c, nullValue());\n assertThat(c, not(hasItem(o)));\n }\n }\n\nThis inspection requires that the Hamcrest library is available on the classpath.\n\nUse the **Statically import matcher's methods** option to specify if you want the quick-fix to statically import the Hamcrest matcher methods." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MigrateAssertToMatcherAssert", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages/Test frameworks", + "index": 94, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InstanceVariableInitialization", + "shortDescription": { + "text": "Instance field may not be initialized" + }, + "fullDescription": { + "text": "Reports instance variables that may be uninitialized upon object initialization. Example: 'class Foo {\n public int bar;\n\n static { }\n }' Note that this inspection uses a very conservative dataflow algorithm and may incorrectly report instance variables as uninitialized. Variables reported as initialized will always be initialized. Use the Ignore primitive fields option to ignore uninitialized primitive fields.", + "markdown": "Reports instance variables that may be uninitialized upon object initialization.\n\n**Example:**\n\n\n class Foo {\n public int bar;\n\n static { }\n }\n\nNote that this inspection uses a very conservative dataflow algorithm and may incorrectly report instance variables as uninitialized. Variables\nreported as initialized will always be initialized.\n\nUse the **Ignore primitive fields** option to ignore uninitialized primitive fields." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "InstanceVariableMayNotBeInitialized", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Initialization", + "index": 28, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CustomClassloader", + "shortDescription": { + "text": "Custom 'ClassLoader' is declared" + }, + "fullDescription": { + "text": "Reports user-defined subclasses of 'java.lang.ClassLoader'. While not necessarily representing a security hole, such classes should be thoroughly inspected for possible security issues.", + "markdown": "Reports user-defined subclasses of `java.lang.ClassLoader`.\n\n\nWhile not necessarily representing a security hole, such classes should be thoroughly\ninspected for possible security issues." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CustomClassloader", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Security", + "index": 30, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IfStatementWithTooManyBranches", + "shortDescription": { + "text": "'if' statement with too many branches" + }, + "fullDescription": { + "text": "Reports 'if' statements with too many branches. Such statements may be confusing and are often a sign of inadequate levels of design abstraction. Use the Maximum number of branches field to specify the maximum number of branches an 'if' statement is allowed to have.", + "markdown": "Reports `if` statements with too many branches.\n\nSuch statements may be confusing and are often a sign of inadequate levels of design\nabstraction.\n\n\nUse the **Maximum number of branches** field to specify the maximum number of branches an `if` statement is allowed to have." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "IfStatementWithTooManyBranches", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessarySuperConstructor", + "shortDescription": { + "text": "Unnecessary call to 'super()'" + }, + "fullDescription": { + "text": "Reports calls to no-arg superclass constructors during object construction. Such calls are unnecessary and may be removed. Example: 'class Foo {\n Foo() {\n super();\n }\n }' After the quick-fix is applied: 'class Foo {\n Foo() {\n }\n }'", + "markdown": "Reports calls to no-arg superclass constructors during object construction.\n\nSuch calls are unnecessary and may be removed.\n\n**Example:**\n\n\n class Foo {\n Foo() {\n super();\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo {\n Foo() {\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryCallToSuper", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ThisEscapedInConstructor", + "shortDescription": { + "text": "'this' reference escaped in object construction" + }, + "fullDescription": { + "text": "Reports possible escapes of 'this' during the object initialization. The escapes occur when 'this' is used as a method argument or an object of assignment in a constructor or initializer. Such escapes may result in subtle bugs, as the object is now available in the context where it is not guaranteed to be initialized. Example: 'class Foo {\n {\n System.out.println(this);\n }\n }'", + "markdown": "Reports possible escapes of `this` during the object initialization. The escapes occur when `this` is used as a method argument or an object of assignment in a constructor or initializer. Such escapes may result in subtle bugs, as the object is now available in the context where it is not guaranteed to be initialized.\n\n**Example:**\n\n\n class Foo {\n {\n System.out.println(this);\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ThisEscapedInObjectConstruction", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Initialization", + "index": 28, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonSerializableObjectPassedToObjectStream", + "shortDescription": { + "text": "Non-serializable object passed to 'ObjectOutputStream'" + }, + "fullDescription": { + "text": "Reports non-'Serializable' objects used as arguments to 'java.io.ObjectOutputStream.write()'. Such calls will result in runtime exceptions. This inspection assumes objects of the types 'java.util.Collection' and 'java.util.Map' to be 'Serializable', unless the types they are declared in are non-'Serializable'. Example: 'public class IWantToSerializeThis {\n public static void main(String[] args) throws IOException {\n try(var stream = new ObjectOutputStream(Files.newOutputStream(Paths.get(\"output\")))) {\n // Warning -- will fail with NotSerializableException\n stream.writeObject(new IWantToSerializeThis());\n }\n }\n }'", + "markdown": "Reports non-`Serializable` objects used as arguments to `java.io.ObjectOutputStream.write()`. Such calls will result in runtime exceptions.\n\n\nThis inspection assumes objects of the types `java.util.Collection` and\n`java.util.Map` to be `Serializable`, unless the types\nthey are declared in are non-`Serializable`.\n\n**Example:**\n\n\n public class IWantToSerializeThis {\n public static void main(String[] args) throws IOException {\n try(var stream = new ObjectOutputStream(Files.newOutputStream(Paths.get(\"output\")))) {\n // Warning -- will fail with NotSerializableException\n stream.writeObject(new IWantToSerializeThis());\n }\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonSerializableObjectPassedToObjectStream", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Serialization issues", + "index": 18, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IfCanBeSwitch", + "shortDescription": { + "text": "'if' can be replaced with 'switch'" + }, + "fullDescription": { + "text": "Reports 'if' statements that can be replaced with 'switch' statements. The replacement result is usually shorter and clearer. Example: 'void test(String str) {\n if (str.equals(\"1\")) {\n System.out.println(1);\n } else if (str.equals(\"2\")) {\n System.out.println(2);\n } else if (str.equals(\"3\")) {\n System.out.println(3);\n } else {\n System.out.println(4);\n }\n }' After the quick-fix is applied: 'void test(String str) {\n switch (str) {\n case \"1\" -> System.out.println(1);\n case \"2\" -> System.out.println(2);\n case \"3\" -> System.out.println(3);\n default -> System.out.println(4);\n }\n }' This inspection only reports if the language level of the project or module is 7 or higher. Use the Minimum number of 'if' condition branches field to specify the minimum number of 'if' condition branches for an 'if' statement to have to be reported. Note that the terminal 'else' branch (without 'if') is not counted. Use the Suggest switch on numbers option to enable the suggestion of 'switch' statements on primitive and boxed numbers and characters. Use the Suggest switch on enums option to enable the suggestion of 'switch' statements on 'enum' constants. Use the Only suggest on null-safe expressions option to suggest 'switch' statements that can't introduce a 'NullPointerException' only.", + "markdown": "Reports `if` statements that can be replaced with `switch` statements.\n\nThe replacement result is usually shorter and clearer.\n\n**Example:**\n\n\n void test(String str) {\n if (str.equals(\"1\")) {\n System.out.println(1);\n } else if (str.equals(\"2\")) {\n System.out.println(2);\n } else if (str.equals(\"3\")) {\n System.out.println(3);\n } else {\n System.out.println(4);\n }\n }\n\nAfter the quick-fix is applied:\n\n\n void test(String str) {\n switch (str) {\n case \"1\" -> System.out.println(1);\n case \"2\" -> System.out.println(2);\n case \"3\" -> System.out.println(3);\n default -> System.out.println(4);\n }\n }\n \nThis inspection only reports if the language level of the project or module is 7 or higher.\n\nUse the **Minimum number of 'if' condition branches** field to specify the minimum number of `if` condition branches\nfor an `if` statement to have to be reported. Note that the terminal `else` branch (without `if`) is not counted.\n\n\nUse the **Suggest switch on numbers** option to enable the suggestion of `switch` statements on\nprimitive and boxed numbers and characters.\n\n\nUse the **Suggest switch on enums** option to enable the suggestion of `switch` statements on\n`enum` constants.\n\n\nUse the **Only suggest on null-safe expressions** option to suggest `switch` statements that can't introduce a `NullPointerException` only." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "IfCanBeSwitch", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids", + "index": 52, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonPublicClone", + "shortDescription": { + "text": "'clone()' method not 'public'" + }, + "fullDescription": { + "text": "Reports 'clone()' methods that are 'protected' and not 'public'. When overriding the 'clone()' method from 'java.lang.Object', it is expected to make the method 'public', so that it is accessible from non-subclasses outside the package.", + "markdown": "Reports `clone()` methods that are `protected` and not `public`.\n\nWhen overriding the `clone()` method from `java.lang.Object`, it is expected to make the method `public`,\nso that it is accessible from non-subclasses outside the package." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonPublicClone", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Cloning issues", + "index": 82, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InfiniteRecursion", + "shortDescription": { + "text": "Infinite recursion" + }, + "fullDescription": { + "text": "Reports methods that call themselves infinitely unless an exception is thrown. Methods reported by this inspection cannot return normally. While such behavior may be intended, in many cases this is just an oversight. Example: 'int baz() {\n return baz();\n }'", + "markdown": "Reports methods that call themselves infinitely unless an exception is thrown.\n\n\nMethods reported by this inspection cannot return normally.\nWhile such behavior may be intended, in many cases this is just an oversight.\n\n**Example:**\n\n int baz() {\n return baz();\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "InfiniteRecursion", + "cweIds": [ + 674, + 835 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UseOfObsoleteDateTimeApi", + "shortDescription": { + "text": "Use of obsolete date-time API" + }, + "fullDescription": { + "text": "Reports usages of 'java.util.Date', 'java.util.Calendar', 'java.util.GregorianCalendar', 'java.util.TimeZone', and 'java.util.SimpleTimeZone'. While still supported, these classes were made obsolete by the JDK8 Date-Time API and should probably not be used in new development.", + "markdown": "Reports usages of `java.util.Date`, `java.util.Calendar`, `java.util.GregorianCalendar`, `java.util.TimeZone`, and `java.util.SimpleTimeZone`.\n\nWhile still supported, these classes were made obsolete by the JDK8 Date-Time API and should probably\nnot be used in new development." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UseOfObsoleteDateTimeApi", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code maturity", + "index": 50, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NullArgumentToVariableArgMethod", + "shortDescription": { + "text": "Confusing argument to varargs method" + }, + "fullDescription": { + "text": "Reports calls to variable arity methods that have a single argument in the vararg parameter position, which is either a 'null' or an array of a subtype of the vararg parameter. Such an argument may be confusing as it is unclear if a varargs or non-varargs call is desired. Example: 'String[] ss = new String[]{\"foo\", \"bar\"};\n System.out.printf(\"%s\", ss);' In this example only the first element of the array will be printed, not the entire array.", + "markdown": "Reports calls to variable arity methods that have a single argument in the vararg parameter position, which is either a `null` or an array of a subtype of the vararg parameter. Such an argument may be confusing as it is unclear if a varargs or non-varargs call is desired.\n\n**Example:**\n\n\n String[] ss = new String[]{\"foo\", \"bar\"};\n System.out.printf(\"%s\", ss);\n\nIn this example only the first element of the array will be printed, not the entire array." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ConfusingArgumentToVarargsMethod", + "cweIds": [ + 628 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ModuleWithTooFewClasses", + "shortDescription": { + "text": "Module with too few classes" + }, + "fullDescription": { + "text": "Reports modules that contain too few classes. Overly small modules may indicate a too fragmented design. Java, Kotlin and Groovy classes are counted. Available only from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name and isn't reported in the editor. Use the Minimum number of classes field to specify the minimum number of classes a module may have.", + "markdown": "Reports modules that contain too few classes. Overly small modules may indicate a too fragmented design. Java, Kotlin and Groovy classes are counted.\n\nAvailable only from **Code \\| Inspect Code** or\n**Code \\| Analyze Code \\| Run Inspection by Name** and isn't reported in the editor.\n\nUse the **Minimum number of classes** field to specify the minimum number of classes a module may have." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ModuleWithTooFewClasses", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Modularization issues", + "index": 69, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OverloadedVarargsMethod", + "shortDescription": { + "text": "Overloaded varargs method" + }, + "fullDescription": { + "text": "Reports varargs methods with the same name as other methods in the class or in a superclass. Overloaded methods that take a variable number of arguments can be very confusing because it is often unclear which overload gets called. Example: 'public void execute(Runnable... r) {} // warning\n public void execute(Runnable r1, Runnable r2) {}' Use the option to ignore overloaded methods whose parameter types are definitely incompatible.", + "markdown": "Reports varargs methods with the same name as other methods in the class or in a superclass. Overloaded methods that take a variable number of arguments can be very confusing because it is often unclear which overload gets called.\n\n**Example:**\n\n\n public void execute(Runnable... r) {} // warning\n public void execute(Runnable r1, Runnable r2) {}\n\n\nUse the option to ignore overloaded methods whose parameter types are definitely incompatible." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OverloadedVarargsMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Naming conventions/Method", + "index": 88, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AnonymousInnerClassMayBeStatic", + "shortDescription": { + "text": "Anonymous class may be a named 'static' inner class" + }, + "fullDescription": { + "text": "Reports anonymous classes that may be safely replaced with 'static' inner classes. An anonymous class may be a 'static' inner class if it doesn't explicitly reference its enclosing instance or local classes from its surrounding method. A 'static' inner class does not keep an implicit reference to its enclosing instance. This prevents a common cause of memory leaks and uses less memory per class instance. Since Java 18, only serializable anonymous classes keep an implicit reference to its enclosing instance, if this reference is not used. So, if module language level is Java 18 or higher, this inspection reports serializable classes only. The quick-fix extracts the anonymous class into a named 'static' inner class. Example: 'void sample() {\n Thread thread = new Thread(new Runnable() {\n @Override\n public void run() {\n }\n });\n }' After the quick-fix is applied: 'void sample() {\n Thread thread = new Thread(new Task());\n }\n\n private static class Task implements Runnable {\n @Override\n public void run() {\n }\n }'", + "markdown": "Reports anonymous classes that may be safely replaced with `static` inner classes. An anonymous class may be a `static` inner class if it doesn't explicitly reference its enclosing instance or local classes from its surrounding method.\n\n\nA `static` inner class does not keep an implicit reference to its enclosing instance.\nThis prevents a common cause of memory leaks and uses less memory per class instance.\n\n\nSince Java 18, only serializable anonymous classes keep an implicit reference to its enclosing instance,\nif this reference is not used. So, if module language level is Java 18 or higher,\nthis inspection reports serializable classes only.\n\nThe quick-fix extracts the anonymous class into a named `static` inner class.\n\n**Example:**\n\n\n void sample() {\n Thread thread = new Thread(new Runnable() {\n @Override\n public void run() {\n }\n });\n }\n\nAfter the quick-fix is applied:\n\n\n void sample() {\n Thread thread = new Thread(new Task());\n }\n\n private static class Task implements Runnable {\n @Override\n public void run() {\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AnonymousInnerClassMayBeStatic", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Memory", + "index": 73, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DeprecatedIsStillUsed", + "shortDescription": { + "text": "Deprecated member is still used" + }, + "fullDescription": { + "text": "Reports deprecated classes, methods, and fields that are used in your code nonetheless. Example: 'class MyCode {\n @Deprecated\n void oldMethod() {}// warning: \"Deprecated member is still used\"\n\n void newMethod() {\n oldMethod(); // forgotten usage\n }\n }' Usages within deprecated elements are ignored. NOTE: Due to performance reasons, a non-private member is checked only when its name rarely occurs in the project.", + "markdown": "Reports deprecated classes, methods, and fields that are used in your code nonetheless.\n\nExample:\n\n\n class MyCode {\n @Deprecated\n void oldMethod() {}// warning: \"Deprecated member is still used\"\n\n void newMethod() {\n oldMethod(); // forgotten usage\n }\n }\n\nUsages within deprecated elements are ignored.\n\n**NOTE:** Due to performance reasons, a non-private member is checked only when its name rarely occurs in the project." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "DeprecatedIsStillUsed", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code maturity", + "index": 50, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PublicConstructor", + "shortDescription": { + "text": "'public' constructor can be replaced with factory method" + }, + "fullDescription": { + "text": "Reports 'public' constructors. Some coding standards discourage the use of 'public' constructors and recommend 'static' factory methods instead. This way the implementation can be swapped out without affecting the call sites. Example: 'class Test {\n private String name;\n\n public Test(String name) {\n this.name = name;\n }\n\n public void test() {\n System.out.println(name);\n }\n\n public static void main(String[] args) {\n new Test(\"str\").test();\n }\n }' After quick-fix is applied: 'class Test {\n private String name;\n\n private Test(String name) {\n this.name = name;\n }\n\n public static Test getInstance(String name) {\n return new Test(name);\n }\n\n public void test() {\n System.out.println(name);\n }\n\n public static void main(String[] args) {\n getInstance(\"str\").test();\n }\n }'", + "markdown": "Reports `public` constructors.\n\nSome coding standards discourage the use of `public` constructors and recommend\n`static` factory methods instead.\nThis way the implementation can be swapped out without affecting the call sites.\n\n**Example:**\n\n\n class Test {\n private String name;\n\n public Test(String name) {\n this.name = name;\n }\n\n public void test() {\n System.out.println(name);\n }\n\n public static void main(String[] args) {\n new Test(\"str\").test();\n }\n }\n\nAfter quick-fix is applied:\n\n\n class Test {\n private String name;\n\n private Test(String name) {\n this.name = name;\n }\n\n public static Test getInstance(String name) {\n return new Test(name);\n }\n\n public void test() {\n System.out.println(name);\n }\n\n public static void main(String[] args) {\n getInstance(\"str\").test();\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PublicConstructor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantComparatorComparing", + "shortDescription": { + "text": "Comparator method can be simplified" + }, + "fullDescription": { + "text": "Reports 'Comparator' combinator constructs that can be simplified. Example: 'c.thenComparing(Comparator.comparing(function));\n\n Comparator.comparing(Map.Entry::getKey);\n\n Collections.max(list, Comparator.reverseOrder());' After the quick-fixes are applied: 'c.thenComparing(function)\n\n Map.Entry.comparingByKey()\n\n Collections.min(list, Comparator.naturalOrder());' New in 2018.1", + "markdown": "Reports `Comparator` combinator constructs that can be simplified.\n\nExample:\n\n\n c.thenComparing(Comparator.comparing(function));\n\n Comparator.comparing(Map.Entry::getKey);\n\n Collections.max(list, Comparator.reverseOrder());\n\nAfter the quick-fixes are applied:\n\n\n c.thenComparing(function)\n\n Map.Entry.comparingByKey()\n\n Collections.min(list, Comparator.naturalOrder());\n\nNew in 2018.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantComparatorComparing", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantEmbeddedExpression", + "shortDescription": { + "text": "Redundant embedded expression in string template" + }, + "fullDescription": { + "text": "Reports redundant embedded expressions in 'STR' templates, such as trivial literals or empty expressions. Example: 'System.out.println(STR.\"Hello \\{\"world\"}\");' After the quick-fix is applied: 'System.out.println(STR.\"Hello world\");' This inspection only reports if the language level of the project or module is 21 or higher. New in 2023.3", + "markdown": "Reports redundant embedded expressions in `STR` templates, such as trivial literals or empty expressions.\n\nExample:\n\n\n System.out.println(STR.\"Hello \\{\"world\"}\");\n\nAfter the quick-fix is applied:\n\n\n System.out.println(STR.\"Hello world\");\n\nThis inspection only reports if the language level of the project or module is 21 or higher.\n\nNew in 2023.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantEmbeddedExpression", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SwitchStatementWithTooFewBranches", + "shortDescription": { + "text": "Minimum 'switch' branches" + }, + "fullDescription": { + "text": "Reports 'switch' statements and expressions with too few 'case' labels, and suggests rewriting them as 'if' and 'else if' statements. Example (minimum branches == 3): 'switch (expression) {\n case \"foo\" -> foo();\n case \"bar\" -> bar();\n }' After the quick-fix is applied: 'if (\"foo\".equals(expression)) {\n foo();\n } else if (\"bar\".equals(expression)) {\n bar();\n }' Exhaustive switch expressions (Java 14+) or pattern switch statements (Java 17 preview) without the 'default' branch are not reported. That's because compile-time exhaustiveness check will be lost when the 'switch' is converted to 'if' which might be undesired. Configure the inspection: Use the Minimum number of branches field to specify the minimum expected number of 'case' labels. Use the Do not report pattern switch statements option to avoid reporting switch statements and expressions that have pattern branches. E.g.: 'String result = switch(obj) {\n case String str -> str.trim();\n default -> \"none\";\n };' It might be preferred to keep the switch even with a single pattern branch, rather than using the 'instanceof' statement.", + "markdown": "Reports `switch` statements and expressions with too few `case` labels, and suggests rewriting them as `if` and `else if` statements.\n\nExample (minimum branches == 3):\n\n\n switch (expression) {\n case \"foo\" -> foo();\n case \"bar\" -> bar();\n }\n\nAfter the quick-fix is applied:\n\n\n if (\"foo\".equals(expression)) {\n foo();\n } else if (\"bar\".equals(expression)) {\n bar();\n }\n\nExhaustive switch expressions (Java 14+) or pattern switch statements (Java 17 preview) without the 'default' branch are not reported.\nThat's because compile-time exhaustiveness check will be lost when the `switch` is converted to `if`\nwhich might be undesired.\n\nConfigure the inspection:\n\nUse the **Minimum number of branches** field to specify the minimum expected number of `case` labels.\n\nUse the **Do not report pattern switch statements** option to avoid reporting switch statements and expressions that\nhave pattern branches. E.g.:\n\n\n String result = switch(obj) {\n case String str -> str.trim();\n default -> \"none\";\n };\n\nIt might be preferred to keep the switch even with a single pattern branch, rather than using the `instanceof` statement." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SwitchStatementWithTooFewBranches", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "VariableNotUsedInsideIf", + "shortDescription": { + "text": "Reference checked for 'null' is not used inside 'if'" + }, + "fullDescription": { + "text": "Reports references to variables that are checked for nullability in the condition of an 'if' statement or conditional expression but not used inside that 'if' statement. Usually this either means that the check is unnecessary or that the variable is not referenced inside the 'if' statement by mistake. Example: 'void test(Integer i) {\n if (i != null) { // here 'i' is not used inside 'if' statement\n System.out.println();\n }\n }'", + "markdown": "Reports references to variables that are checked for nullability in the condition of an `if` statement or conditional expression but not used inside that `if` statement.\n\n\nUsually this either means that\nthe check is unnecessary or that the variable is not referenced inside the\n`if` statement by mistake.\n\n**Example:**\n\n\n void test(Integer i) {\n if (i != null) { // here 'i' is not used inside 'if' statement\n System.out.println();\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "VariableNotUsedInsideIf", + "cweIds": [ + 563 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UsagesOfObsoleteApi", + "shortDescription": { + "text": "Usages of ApiStatus.@Obsolete" + }, + "fullDescription": { + "text": "Reports declarations (classes, methods, fields) annotated as '@ApiStatus.Obsolete'. Sometimes it's impossible to delete the current API, though it might not work correctly, there is a newer, or better, or more generic API. This way, it's a weaker variant of '@Deprecated' annotation. The annotated API is not supposed to be used in the new code, but it's permitted to postpone the migration of the existing code, therefore the usage is not considered a warning.", + "markdown": "Reports declarations (classes, methods, fields) annotated as `@ApiStatus.Obsolete`.\n\n\nSometimes it's impossible to delete the current API, though it might not work correctly, there is a newer, or better, or more generic API.\nThis way, it's a weaker variant of `@Deprecated` annotation.\nThe annotated API is not supposed to be used in the new code, but it's permitted to postpone the migration of the existing code,\ntherefore the usage is not considered a warning." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "UsagesOfObsoleteApi", + "ideaSeverity": "TEXT ATTRIBUTES", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "JVM languages", + "index": 2, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CollectionAddAllCanBeReplacedWithConstructor", + "shortDescription": { + "text": "Redundant 'Collection.addAll()' call" + }, + "fullDescription": { + "text": "Reports 'Collection.addAll()' and 'Map.putAll()' calls immediately after an instantiation of a collection using a no-arg constructor. Such constructs can be replaced with a single call to a parametrized constructor, which simplifies the code. Also, for some collections the replacement might be more performant. Example: 'Set set = new HashSet<>();\n set.addAll(Arrays.asList(\"alpha\", \"beta\", \"gamma\"));' After the quick-fix is applied: 'Set set = new HashSet<>(Arrays.asList(\"alpha\", \"beta\", \"gamma\"));' The JDK collection classes are supported by default. Additionally, you can specify other classes using the Classes to check panel.", + "markdown": "Reports `Collection.addAll()` and `Map.putAll()` calls immediately after an instantiation of a collection using a no-arg constructor.\n\nSuch constructs can be replaced with a single call to a parametrized constructor, which simplifies the code. Also, for some collections the replacement\nmight be more performant.\n\n**Example:**\n\n\n Set set = new HashSet<>();\n set.addAll(Arrays.asList(\"alpha\", \"beta\", \"gamma\"));\n\nAfter the quick-fix is applied:\n\n\n Set set = new HashSet<>(Arrays.asList(\"alpha\", \"beta\", \"gamma\"));\n\n\nThe JDK collection classes are supported by default.\nAdditionally, you can specify other classes using the **Classes to check** panel." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "CollectionAddAllCanBeReplacedWithConstructor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Performance", + "index": 10, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MultipleReturnPointsPerMethod", + "shortDescription": { + "text": "Method with multiple return points" + }, + "fullDescription": { + "text": "Reports methods whose number of 'return' points exceeds the specified maximum. Methods with too many 'return' points may be confusing and hard to refactor. A 'return' point is either a 'return' statement or a falling through the bottom of a 'void' method or constructor. Example: The method below is reported if only two 'return' statements are allowed: 'void doSmth(User[] users) {\n for (User user : users) {\n if (cond1(user)) {\n user.setId(getId());\n return;\n } else if (cond2(user)) {\n if (cond3(user)) {\n user.setId(getId());\n return;\n }\n }\n }\n }' Consider rewriting the method so it becomes easier to understand: 'void doSmth(User[] users) {\n for (User user : users) {\n if (cond1(user) || cond2(user) && cond3(user)) {\n user.setId(getId());\n return;\n }\n }\n }' Configure the inspection: Use the Return point limit field to specify the maximum allowed number of 'return' points for a method. Use the Ignore guard clauses option to ignore guard clauses. A guard clause is an 'if' statement that contains only a 'return' statement Use the Ignore for 'equals()' methods option to ignore 'return' points inside 'equals()' methods.", + "markdown": "Reports methods whose number of `return` points exceeds the specified maximum. Methods with too many `return` points may be confusing and hard to refactor.\n\nA `return` point is either a `return` statement or a falling through the bottom of a\n`void` method or constructor.\n\n**Example:**\n\nThe method below is reported if only two `return` statements are allowed:\n\n\n void doSmth(User[] users) {\n for (User user : users) {\n if (cond1(user)) {\n user.setId(getId());\n return;\n } else if (cond2(user)) {\n if (cond3(user)) {\n user.setId(getId());\n return;\n }\n }\n }\n }\n\nConsider rewriting the method so it becomes easier to understand:\n\n\n void doSmth(User[] users) {\n for (User user : users) {\n if (cond1(user) || cond2(user) && cond3(user)) {\n user.setId(getId());\n return;\n }\n }\n }\n\nConfigure the inspection:\n\n* Use the **Return point limit** field to specify the maximum allowed number of `return` points for a method.\n* Use the **Ignore guard clauses** option to ignore guard clauses. A guard clause is an `if` statement that contains only a `return` statement\n* Use the **Ignore for 'equals()' methods** option to ignore `return` points inside `equals()` methods." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MethodWithMultipleReturnPoints", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Method metrics", + "index": 95, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConstantDeclaredInInterface", + "shortDescription": { + "text": "Constant declared in interface" + }, + "fullDescription": { + "text": "Reports constants ('public static final' fields) declared in interfaces. Some coding standards require declaring constants in abstract classes instead.", + "markdown": "Reports constants (`public static final` fields) declared in interfaces.\n\nSome coding standards require declaring constants in abstract classes instead." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ConstantDeclaredInInterface", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class structure", + "index": 11, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspiciousMethodCalls", + "shortDescription": { + "text": "Suspicious collection method call" + }, + "fullDescription": { + "text": "Reports method calls on parameterized collections, where the actual argument type does not correspond to the collection's elements type. Example: 'List list = getListOfElements();\n list.remove(\"\"); // remove is highlighted' In the inspection settings, you can disable warnings for potentially correct code like the following: 'public boolean accept(Map map, Object key) {\n return map.containsKey(key);\n }'", + "markdown": "Reports method calls on parameterized collections, where the actual argument type does not correspond to the collection's elements type.\n\n**Example:**\n\n\n List list = getListOfElements();\n list.remove(\"\"); // remove is highlighted\n\n\nIn the inspection settings, you can disable warnings for potentially correct code like the following:\n\n\n public boolean accept(Map map, Object key) {\n return map.containsKey(key);\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SuspiciousMethodCalls", + "cweIds": [ + 628 + ], + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Probable bugs", + "index": 13, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ForwardCompatibility", + "shortDescription": { + "text": "Forward compatibility" + }, + "fullDescription": { + "text": "Reports Java code constructs that may fail to compile in future Java versions. The following problems are reported: Uses of 'assert', 'enum' or '_' as an identifier Uses of the 'var', 'yield', or 'record' restricted identifier as a type name Unqualified calls to methods named 'yield' Modifiers on the 'requires java.base' statement inside of 'module-info.java' Redundant semicolons between import statements Example: '// This previously legal class does not compile with Java 14,\n // as 'yield' became a restricted identifier.\n public class yield {}' Fixing these issues timely may simplify migration to future Java versions.", + "markdown": "Reports Java code constructs that may fail to compile in future Java versions.\n\nThe following problems are reported:\n\n* Uses of `assert`, `enum` or `_` as an identifier\n* Uses of the `var`, `yield`, or `record` restricted identifier as a type name\n* Unqualified calls to methods named `yield`\n* Modifiers on the `requires java.base` statement inside of `module-info.java`\n* Redundant semicolons between import statements\n\n**Example:**\n\n\n // This previously legal class does not compile with Java 14,\n // as 'yield' became a restricted identifier.\n public class yield {} \n\nFixing these issues timely may simplify migration to future Java versions." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ForwardCompatibility", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level issues", + "index": 64, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DiamondCanBeReplacedWithExplicitTypeArguments", + "shortDescription": { + "text": "Diamond can be replaced with explicit type arguments" + }, + "fullDescription": { + "text": "Reports instantiation of generic classes in which the <> symbol (diamond) is used instead of type parameters. The quick-fix replaces <> (diamond) with explicit type parameters. Example: 'List list = new ArrayList<>()' After the quick-fix is applied: 'List list = new ArrayList()' Diamond operation appeared in Java 7. This inspection can help to downgrade for backward compatibility with earlier Java versions.", + "markdown": "Reports instantiation of generic classes in which the **\\<\\>** symbol (diamond) is used instead of type parameters.\n\nThe quick-fix replaces **\\<\\>** (diamond) with explicit type parameters.\n\nExample:\n\n List list = new ArrayList<>()\n\nAfter the quick-fix is applied:\n\n List list = new ArrayList()\n\n\n*Diamond operation* appeared in Java 7.\nThis inspection can help to downgrade for backward compatibility with earlier Java versions." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "DiamondCanBeReplacedWithExplicitTypeArguments", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Code style issues", + "index": 12, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UseOfProcessBuilder", + "shortDescription": { + "text": "Use of 'java.lang.ProcessBuilder' class" + }, + "fullDescription": { + "text": "Reports uses of 'java.lang.ProcessBuilder', which might be unportable between operating systems because paths to executables, environment variables, command-line arguments and their escaping might vary depending on the OS.", + "markdown": "Reports uses of `java.lang.ProcessBuilder`, which might be unportable between operating systems because paths to executables, environment variables, command-line arguments and their escaping might vary depending on the OS." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UseOfProcessBuilder", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Portability", + "index": 7, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JavaRequiresAutoModule", + "shortDescription": { + "text": "Dependencies on automatic modules" + }, + "fullDescription": { + "text": "Reports usages of automatic modules in a 'requires' directive. An automatic module is unreliable since it can depend on the types on the class path, and its name and exported packages can change if it's converted into an explicit module. Corresponds to '-Xlint:requires-automatic' and '-Xlint:requires-transitive-automatic' Javac options. The first option increases awareness of when automatic modules are used. The second warns the authors of a module that they're putting the users of that module at risk by establishing implied readability to an automatic module. Example: '//module-info.java\n module org.printer {\n requires transitive drivers.corp.org; // reported in case 'drivers.corp.org' is an automatic module\n }' Use the Highlight only transitive dependencies option to warn only about transitive dependencies.", + "markdown": "Reports usages of automatic modules in a `requires` directive.\n\nAn automatic\nmodule is unreliable since it can depend on the types on the class path,\nand its name and exported packages can change if it's\nconverted into an explicit module.\n\nCorresponds to `-Xlint:requires-automatic` and `-Xlint:requires-transitive-automatic` Javac options.\nThe first option increases awareness of when automatic modules are used.\nThe second warns the authors of a module that they're putting the users of that module at risk by establishing implied readability to an automatic module.\n\n**Example:**\n\n\n //module-info.java\n module org.printer {\n requires transitive drivers.corp.org; // reported in case 'drivers.corp.org' is an automatic module\n }\n\n\nUse the **Highlight only transitive dependencies** option to warn only about transitive dependencies." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "requires-transitive-automatic", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 9", + "index": 80, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ExcessiveRangeCheck", + "shortDescription": { + "text": "Excessive range check" + }, + "fullDescription": { + "text": "Reports condition chains in which a value range is checked and these condition chains can be simplified to a single check. The quick-fix replaces a condition chain with a simplified expression: Example: 'x > 2 && x < 4' After the quick-fix is applied: 'x == 3' Example: 'arr.length == 0 || arr.length > 1' After the quick-fix is applied: 'arr.length != 1' New in 2019.1", + "markdown": "Reports condition chains in which a value range is checked and these condition chains can be simplified to a single check.\n\nThe quick-fix replaces a condition chain with a simplified expression:\n\nExample:\n\n\n x > 2 && x < 4\n\nAfter the quick-fix is applied:\n\n\n x == 3\n\nExample:\n\n\n arr.length == 0 || arr.length > 1\n\nAfter the quick-fix is applied:\n\n\n arr.length != 1\n\nNew in 2019.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ExcessiveRangeCheck", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StaticImport", + "shortDescription": { + "text": "Static import" + }, + "fullDescription": { + "text": "Reports 'import static' statements. Such 'import' statements are not supported under Java 1.4 or earlier JVMs. Configure the inspection: Use the table below to specify the classes that will be ignored by the inspection when used in an 'import static' statement. Use the Ignore single field static imports checkbox to ignore single-field 'import static' statements. Use the Ignore single method static imports checkbox to ignore single-method 'import static' statements.", + "markdown": "Reports `import static` statements.\n\nSuch `import` statements are not supported under Java 1.4 or earlier JVMs.\n\nConfigure the inspection:\n\n* Use the table below to specify the classes that will be ignored by the inspection when used in an `import static` statement.\n* Use the **Ignore single field static imports** checkbox to ignore single-field `import static` statements.\n* Use the **Ignore single method static imports** checkbox to ignore single-method `import static` statements." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StaticImport", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Imports", + "index": 22, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceNullCheck", + "shortDescription": { + "text": "Null check can be replaced with method call" + }, + "fullDescription": { + "text": "Reports 'null' checks that can be replaced with a call to a static method from 'Objects' or 'Stream'. Example: 'if (message == null) {\n application.messageStorage().save(new EmptyMessage());\n } else {\n application.messageStorage().save(message);\n }' After the quick-fix is applied: 'application.messageStorage()\n .save(Objects.requireNonNullElseGet(message, () -> new EmptyMessage()));' Use the Don't warn if the replacement is longer than the original option to ignore the cases when the replacement is longer than the original code. New in 2017.3", + "markdown": "Reports `null` checks that can be replaced with a call to a static method from `Objects` or `Stream`.\n\n**Example:**\n\n\n if (message == null) {\n application.messageStorage().save(new EmptyMessage());\n } else {\n application.messageStorage().save(message);\n }\n\nAfter the quick-fix is applied:\n\n\n application.messageStorage()\n .save(Objects.requireNonNullElseGet(message, () -> new EmptyMessage()));\n\n\nUse the **Don't warn if the replacement is longer than the original** option to ignore the cases when the replacement is longer than the\noriginal code.\n\nNew in 2017.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ReplaceNullCheck", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 9", + "index": 80, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NoExplicitFinalizeCalls", + "shortDescription": { + "text": "'finalize()' called explicitly" + }, + "fullDescription": { + "text": "Reports calls to 'Object.finalize()'. Calling 'Object.finalize()' explicitly may result in objects being placed in an inconsistent state. The garbage collector automatically calls this method on an object when it determines that there are no references to this object. The inspection doesn't report calls to 'super.finalize()' from within implementations of 'finalize()' as they're benign. Example: 'MyObject m = new MyObject();\n m.finalize();\n System.gc()'", + "markdown": "Reports calls to `Object.finalize()`.\n\nCalling `Object.finalize()` explicitly may result in objects being placed in an\ninconsistent state.\nThe garbage collector automatically calls this method on an object when it determines that there are no references to this object.\n\nThe inspection doesn't report calls to `super.finalize()` from within implementations of `finalize()` as\nthey're benign.\n\n**Example:**\n\n\n MyObject m = new MyObject();\n m.finalize();\n System.gc()\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "FinalizeCalledExplicitly", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Finalization", + "index": 66, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NegatedIfElse", + "shortDescription": { + "text": "'if' statement with negated condition" + }, + "fullDescription": { + "text": "Reports 'if' statements that contain 'else' branches and whose conditions are negated. Flipping the order of the 'if' and 'else' branches usually increases the clarity of such statements. There is a fix that inverts the current 'if' statement. Example: 'void m(Object o1, Object o2) {\n if (o1 != o2) {\n System.out.println(1);\n }\n else {\n System.out.println(2);\n }\n }' After applying the quick-fix: 'void m(Object o1, Object o2) {\n if (o1 == o2) {\n System.out.println(2);\n } else {\n System.out.println(1);\n }\n }' Use the Ignore '!= null' comparisons option to ignore comparisons of the '!= null' form. Use the Ignore '!= 0' comparisons option to ignore comparisons of the '!= 0' form.", + "markdown": "Reports `if` statements that contain `else` branches and whose conditions are negated.\n\nFlipping the order of the `if` and `else`\nbranches usually increases the clarity of such statements.\n\nThere is a fix that inverts the current `if` statement.\n\nExample:\n\n\n void m(Object o1, Object o2) {\n if (o1 != o2) {\n System.out.println(1);\n }\n else {\n System.out.println(2);\n }\n }\n\nAfter applying the quick-fix:\n\n\n void m(Object o1, Object o2) {\n if (o1 == o2) {\n System.out.println(2);\n } else {\n System.out.println(1);\n }\n }\n\nUse the **Ignore '!= null' comparisons** option to ignore comparisons of the `!= null` form.\n\nUse the **Ignore '!= 0' comparisons** option to ignore comparisons of the `!= 0` form." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "IfStatementWithNegatedCondition", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Control flow issues", + "index": 26, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FieldCount", + "shortDescription": { + "text": "Class with too many fields" + }, + "fullDescription": { + "text": "Reports classes whose number of fields exceeds the specified maximum. Classes with a large number of fields are often trying to do too much. Consider splitting such a class into multiple smaller classes. Configure the inspection: Use the Field count limit field to specify the maximum allowed number of fields in a class. Use the Include constant fields in count option to indicate whether constant fields should be counted. By default only immutable 'static final' objects are counted as constants. Use the 'static final' fields count as constant option to count any 'static final' field as constant. Use the Include enum constants in count option to specify whether 'enum' constants in 'enum' classes should be counted.", + "markdown": "Reports classes whose number of fields exceeds the specified maximum.\n\nClasses with a large number of fields are often trying to do too much. Consider splitting such a class into multiple smaller classes.\n\nConfigure the inspection:\n\n* Use the **Field count limit** field to specify the maximum allowed number of fields in a class.\n* Use the **Include constant fields in count** option to indicate whether constant fields should be counted.\n* By default only immutable `static final` objects are counted as constants. Use the **'static final' fields count as constant** option to count any `static final` field as constant.\n* Use the **Include enum constants in count** option to specify whether `enum` constants in `enum` classes should be counted." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClassWithTooManyFields", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Class metrics", + "index": 87, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StringConcatenation", + "shortDescription": { + "text": "String concatenation" + }, + "fullDescription": { + "text": "Reports 'String' concatenations. Concatenation might be incorrect in an internationalized environment and could be replaced by usages of 'java.text.MessageFormat' or similar classes. Example: 'String getMessage(String string, int number) {\n return string + number;\n }'", + "markdown": "Reports `String` concatenations. Concatenation might be incorrect in an internationalized environment and could be replaced by usages of `java.text.MessageFormat` or similar classes.\n\n**Example:**\n\n\n String getMessage(String string, int number) {\n return string + number;\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StringConcatenation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Internationalization", + "index": 9, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassCanBeRecord", + "shortDescription": { + "text": "Class can be a record" + }, + "fullDescription": { + "text": "Suggests replacing classes with records. The inspection can be useful if you need to focus on modeling immutable data rather than extensible behavior. Automatic implementation of data-driven methods, such as equals and accessors, helps to get rid of boilerplate. Note that not every class can be a record. Here are some of the restrictions: A class must contain no inheritors and must be a top-level class. All the non-static fields in class must be final. Class must contain no instance initializers, generic constructors, nor native methods. To get a full list of the restrictions, refer to the Oracle documentation. Example: 'class Point {\n private final double x;\n private final double y;\n\n Point(double x, double y) {\n this.x = x;\n this.y = y;\n }\n\n double getX() {\n return x;\n }\n\n double getY() {\n return y;\n }\n }' After the quick-fix is applied: 'record Point(int x, int y) {\n }' Enable the Suggest renaming get/is-accessors option to allow renaming 'getX()'/'isX()' accessors to 'x()' automatically. Use the When conversion makes a member more accessible options to specify if the conversion may violate class encapsulation: Choose Do not suggest conversion option to never violate class encapsulation Choose Show affected members in conflicts view option to apply conversion with notification about encapsulation violation issues Choose Convert silently option to apply conversion silently whether encapsulation violation issues exist or not Use the Suppress conversion if class is annotated by list to exclude classes from conversion when annotated by annotations matching the specified patterns. This inspection only reports if the language level of the project or module is 16 or higher. New in 2020.3", + "markdown": "Suggests replacing classes with records.\n\nThe inspection can be useful if you need to focus on modeling immutable data rather than extensible behavior.\nAutomatic implementation of data-driven methods, such as equals and accessors, helps to get rid of boilerplate.\n\n\nNote that not every class can be a record. Here are some of the restrictions:\n\n* A class must contain no inheritors and must be a top-level class.\n* All the non-static fields in class must be final.\n* Class must contain no instance initializers, generic constructors, nor native methods.\n\nTo get a full list of the restrictions, refer to the\n[Oracle documentation](https://docs.oracle.com/javase/specs/jls/se15/preview/specs/records-jls.html).\n\nExample:\n\n\n class Point {\n private final double x;\n private final double y;\n\n Point(double x, double y) {\n this.x = x;\n this.y = y;\n }\n\n double getX() {\n return x;\n }\n\n double getY() {\n return y;\n }\n }\n\nAfter the quick-fix is applied:\n\n\n record Point(int x, int y) {\n }\n\nEnable the **Suggest renaming get/is-accessors** option to allow renaming `getX()`/`isX()` accessors to `x()` automatically.\n\n\nUse the **When conversion makes a member more accessible** options to specify if the conversion may violate class encapsulation:\n\n* Choose **Do not suggest conversion** option to never violate class encapsulation\n* Choose **Show affected members in conflicts view** option to apply conversion with notification about encapsulation violation issues\n* Choose **Convert silently** option to apply conversion silently whether encapsulation violation issues exist or not\n\nUse the **Suppress conversion if class is annotated by** list to exclude classes from conversion when annotated by annotations matching the specified patterns.\n\nThis inspection only reports if the language level of the project or module is 16 or higher.\n\nNew in 2020.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ClassCanBeRecord", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Java language level migration aids/Java 16", + "index": 111, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConstantAssertArgument", + "shortDescription": { + "text": "Constant assert argument" + }, + "fullDescription": { + "text": "Reports constant arguments in 'assertTrue()', 'assertFalse()', 'assertNull()', and 'assertNotNull()' calls. Calls to these methods with constant arguments will either always succeed or always fail. Such statements can easily be left over after refactoring and are probably not intended. Example: 'assertNotNull(\"foo\");'", + "markdown": "Reports constant arguments in `assertTrue()`, `assertFalse()`, `assertNull()`, and `assertNotNull()` calls.\n\n\nCalls to these methods with\nconstant arguments will either always succeed or always fail.\nSuch statements can easily be left over after refactoring and are probably not intended.\n\n**Example:**\n\n\n assertNotNull(\"foo\");\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ConstantAssertArgument", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Test frameworks", + "index": 96, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + } + ], + "language": "en-US", + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "isComprehensive": false + }, + { + "name": "org.jetbrains.kotlin", + "version": "233.14714-IJ", + "rules": [ + { + "id": "RedundantRunCatching", + "shortDescription": { + "text": "Redundant 'runCatching' call" + }, + "fullDescription": { + "text": "Reports 'runCatching' calls that are immediately followed by 'getOrThrow'. Such calls can be replaced with just 'run'. Example: 'fun foo() = runCatching { doSomething() }.getOrThrow()' After the quick-fix is applied: 'fun foo() = run { doSomething() }'", + "markdown": "Reports `runCatching` calls that are immediately followed by `getOrThrow`. Such calls can be replaced with just `run`.\n\n**Example:**\n\n\n fun foo() = runCatching { doSomething() }.getOrThrow()\n\nAfter the quick-fix is applied:\n\n\n fun foo() = run { doSomething() }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RedundantRunCatching", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SimpleRedundantLet", + "shortDescription": { + "text": "Redundant receiver-based 'let' call" + }, + "fullDescription": { + "text": "Reports redundant receiver-based 'let' calls. The quick-fix removes the redundant 'let' call. Example: 'fun test(s: String?): Int? = s?.let { it.length }' After the quick-fix is applied: 'fun test(s: String?): Int? = s?.length'", + "markdown": "Reports redundant receiver-based `let` calls.\n\nThe quick-fix removes the redundant `let` call.\n\n**Example:**\n\n\n fun test(s: String?): Int? = s?.let { it.length }\n\nAfter the quick-fix is applied:\n\n\n fun test(s: String?): Int? = s?.length\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SimpleRedundantLet", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RemoveSingleExpressionStringTemplate", + "shortDescription": { + "text": "Redundant string template" + }, + "fullDescription": { + "text": "Reports single-expression string templates that can be safely removed. Example: 'val x = \"Hello\"\n val y = \"$x\"' After the quick-fix is applied: 'val x = \"Hello\"\n val y = x // <== Updated'", + "markdown": "Reports single-expression string templates that can be safely removed.\n\n**Example:**\n\n val x = \"Hello\"\n val y = \"$x\"\n\nAfter the quick-fix is applied:\n\n val x = \"Hello\"\n val y = x // <== Updated\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RemoveSingleExpressionStringTemplate", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonExhaustiveWhenStatementMigration", + "shortDescription": { + "text": "Non-exhaustive 'when' statements will be prohibited since 1.7" + }, + "fullDescription": { + "text": "Reports a non-exhaustive 'when' statements that will lead to compilation error since 1.7. Motivation types: Problematic/meaningless usage patterns need to be discouraged/blocked (e.g. counterintuitive behaviors) Code is error-prone Inconsistency in the design (things are done differently in different contexts) Impact types: Compilation. Some code that used to compile won't compile any more There were cases when such code worked with no exceptions Some such code could compile without any warnings More details: KT-47709: Make when statements with enum, sealed, and Boolean subjects exhaustive by default The quick-fix adds the missing 'else -> {}' branch. Example: 'sealed class Base {\n class A : Base()\n class B : Base()\n }\n\n fun test(base: Base) {\n when (base) {\n is Base.A -> \"\"\n }\n }' After the quick-fix is applied: 'sealed class Base {\n class A : Base()\n class B : Base()\n }\n\n fun test(base: Base) {\n when (base) {\n is Base.A -> \"\"\n else -> {}\n }\n }' This inspection only reports if the Kotlin language level of the project or module is 1.6 or higher.", + "markdown": "Reports a non-exhaustive `when` statements that will lead to compilation error since 1.7.\n\nMotivation types:\n\n* Problematic/meaningless usage patterns need to be discouraged/blocked (e.g. counterintuitive behaviors)\n * Code is error-prone\n* Inconsistency in the design (things are done differently in different contexts)\n\nImpact types:\n\n* Compilation. Some code that used to compile won't compile any more\n * There were cases when such code worked with no exceptions\n * Some such code could compile without any warnings\n\n**More details:** [KT-47709: Make when statements with enum, sealed, and Boolean subjects exhaustive by default](https://youtrack.jetbrains.com/issue/KT-47709)\n\nThe quick-fix adds the missing `else -> {}` branch.\n\n**Example:**\n\n\n sealed class Base {\n class A : Base()\n class B : Base()\n }\n\n fun test(base: Base) {\n when (base) {\n is Base.A -> \"\"\n }\n }\n\nAfter the quick-fix is applied:\n\n\n sealed class Base {\n class A : Base()\n class B : Base()\n }\n\n fun test(base: Base) {\n when (base) {\n is Base.A -> \"\"\n else -> {}\n }\n }\n\nThis inspection only reports if the Kotlin language level of the project or module is 1.6 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonExhaustiveWhenStatementMigration", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Migration", + "index": 16, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConvertLambdaToReference", + "shortDescription": { + "text": "Can be replaced with function reference" + }, + "fullDescription": { + "text": "Reports function literal expressions that can be replaced with function references. Replacing lambdas with function references often makes code look more concise and understandable. Example: 'fun Int.isEven() = this % 2 == 0\n\n fun example() {\n val numbers = listOf(1, 2, 4, 7, 9, 10)\n val evenNumbers = numbers.filter { it.isEven() }\n }' After the quick-fix is applied: 'fun Int.isEven() = this % 2 == 0\n\n fun example() {\n val numbers = listOf(1, 2, 4, 7, 9, 10)\n val evenNumbers = numbers.filter(Int::isEven)\n }'", + "markdown": "Reports function literal expressions that can be replaced with function references.\n\nReplacing lambdas with function references often makes code look more concise and understandable.\n\n**Example:**\n\n\n fun Int.isEven() = this % 2 == 0\n\n fun example() {\n val numbers = listOf(1, 2, 4, 7, 9, 10)\n val evenNumbers = numbers.filter { it.isEven() }\n }\n\nAfter the quick-fix is applied:\n\n\n fun Int.isEven() = this % 2 == 0\n\n fun example() {\n val numbers = listOf(1, 2, 4, 7, 9, 10)\n val evenNumbers = numbers.filter(Int::isEven)\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ConvertLambdaToReference", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantSetter", + "shortDescription": { + "text": "Redundant property setter" + }, + "fullDescription": { + "text": "Reports redundant property setters. Setter is considered to be redundant in one of the following cases: Setter has no body. Accessor visibility isn't changed, declaration isn't 'external' and has no annotations. 'var myPropWithRedundantSetter: Int = 0\n set // redundant\n\n var myPropA: Int = 0\n private set // OK - property visibility is changed to private\n\n var myPropB: Int = 0\n external set // OK - implemented not in Kotlin (external)\n\n var myPropC: Int = 0\n @Inject set // OK - accessor is annotated' Setter body is a block with a single statement assigning the parameter to the backing field. 'var prop: Int = 0\n set(value) { // redundant\n field = value\n }'", + "markdown": "Reports redundant property setters.\n\n\nSetter is considered to be redundant in one of the following cases:\n\n1. Setter has no body. Accessor visibility isn't changed, declaration isn't `external` and has no annotations.\n\n\n var myPropWithRedundantSetter: Int = 0\n set // redundant\n\n var myPropA: Int = 0\n private set // OK - property visibility is changed to private\n\n var myPropB: Int = 0\n external set // OK - implemented not in Kotlin (external)\n\n var myPropC: Int = 0\n @Inject set // OK - accessor is annotated\n \n2. Setter body is a block with a single statement assigning the parameter to the backing field.\n\n\n var prop: Int = 0\n set(value) { // redundant\n field = value\n }\n \n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RedundantSetter", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IncompleteDestructuring", + "shortDescription": { + "text": "Incomplete destructuring declaration" + }, + "fullDescription": { + "text": "Reports incomplete destructuring declaration. Example: 'data class Person(val name: String, val age: Int)\n val person = Person(\"\", 0)\n val (name) = person' The quick fix completes destructuring declaration with new variables: 'data class Person(val name: String, val age: Int)\n val person = Person(\"\", 0)\n val (name, age) = person'", + "markdown": "Reports incomplete destructuring declaration.\n\n**Example:**\n\n\n data class Person(val name: String, val age: Int)\n val person = Person(\"\", 0)\n val (name) = person\n\nThe quick fix completes destructuring declaration with new variables:\n\n\n data class Person(val name: String, val age: Int)\n val person = Person(\"\", 0)\n val (name, age) = person\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "IncompleteDestructuring", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ScopeFunctionConversion", + "shortDescription": { + "text": "Scope function can be converted to another one" + }, + "fullDescription": { + "text": "Reports scope functions ('let', 'run', 'apply', 'also') that can be converted between each other. Using corresponding functions makes your code simpler. The quick-fix replaces the scope function to another one. Example: 'val x = \"\".let {\n it.length\n }' After the quick-fix is applied: 'val x = \"\".run {\n length\n }'", + "markdown": "Reports scope functions (`let`, `run`, `apply`, `also`) that can be converted between each other.\n\nUsing corresponding functions makes your code simpler.\n\nThe quick-fix replaces the scope function to another one.\n\n**Example:**\n\n\n val x = \"\".let {\n it.length\n }\n\nAfter the quick-fix is applied:\n\n\n val x = \"\".run {\n length\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ScopeFunctionConversion", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TrailingComma", + "shortDescription": { + "text": "Trailing comma recommendations" + }, + "fullDescription": { + "text": "Reports trailing commas that do not follow the recommended style guide.", + "markdown": "Reports trailing commas that do not follow the recommended [style guide](https://kotlinlang.org/docs/coding-conventions.html#trailing-commas)." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "TrailingComma", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FoldInitializerAndIfToElvis", + "shortDescription": { + "text": "If-Null return/break/... foldable to '?:'" + }, + "fullDescription": { + "text": "Reports an 'if' expression that checks variable being null or not right after initializing it that can be converted into an elvis operator in the initializer. Example: 'fun test(foo: Int?, bar: Int): Int {\n var i = foo\n if (i == null) {\n return bar\n }\n return i\n }' The quick-fix converts the 'if' expression with an initializer into an elvis expression: 'fun test(foo: Int?, bar: Int): Int {\n var i = foo ?: return bar\n return i\n }'", + "markdown": "Reports an `if` expression that checks variable being null or not right after initializing it that can be converted into an elvis operator in the initializer.\n\n**Example:**\n\n\n fun test(foo: Int?, bar: Int): Int {\n var i = foo\n if (i == null) {\n return bar\n }\n return i\n }\n\nThe quick-fix converts the `if` expression with an initializer into an elvis expression:\n\n\n fun test(foo: Int?, bar: Int): Int {\n var i = foo ?: return bar\n return i\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "FoldInitializerAndIfToElvis", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CanSealedSubClassBeObject", + "shortDescription": { + "text": "Sealed subclass without state and overridden equals" + }, + "fullDescription": { + "text": "Reports direct inheritors of 'sealed' classes that have no state and overridden 'equals()' method. It's highly recommended to override 'equals()' to provide comparison stability, or convert the 'class' to an 'object' to reach the same effect. Example: 'sealed class Receiver {\n class Everyone : Receiver()\n class User(val id: Int) : Receiver()\n }' The quick-fix converts a 'class' into an 'object': 'sealed class Receiver {\n object Everyone : Receiver()\n class User(val id: Int) : Receiver()\n }'", + "markdown": "Reports direct inheritors of `sealed` classes that have no state and overridden `equals()` method.\n\nIt's highly recommended to override `equals()` to provide comparison stability, or convert the `class` to an `object` to reach the same effect.\n\n**Example:**\n\n\n sealed class Receiver {\n class Everyone : Receiver()\n class User(val id: Int) : Receiver()\n }\n\nThe quick-fix converts a `class` into an `object`:\n\n\n sealed class Receiver {\n object Everyone : Receiver()\n class User(val id: Int) : Receiver()\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "note", + "parameters": { + "suppressToolId": "CanSealedSubClassBeObject", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InlineClassDeprecatedMigration", + "shortDescription": { + "text": "Inline classes are deprecated since 1.5" + }, + "fullDescription": { + "text": "Reports inline classes that are deprecated and cause compilation warnings in Kotlin 1.5 and later. See What's new in Kotlin 1.5.0 Example: 'inline class Password(val s: String)' After the quick-fix is applied: '@JvmInline\n value class Password(val s: String)' Inspection is available for Kotlin language level starting from 1.5.", + "markdown": "Reports inline classes that are deprecated and cause compilation warnings in Kotlin 1.5 and later.\nSee [What's new in Kotlin 1.5.0](https://kotlinlang.org/docs/whatsnew15.html#inline-classes)\n\nExample:\n\n\n inline class Password(val s: String)\n\nAfter the quick-fix is applied:\n\n\n @JvmInline\n value class Password(val s: String)\n\nInspection is available for Kotlin language level starting from 1.5." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "InlineClassDeprecatedMigration", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Migration", + "index": 16, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PropertyName", + "shortDescription": { + "text": "Property naming convention" + }, + "fullDescription": { + "text": "Reports property names that do not follow the recommended naming conventions. Consistent naming allows for easier code reading and understanding. According to the Kotlin official style guide, property names should start with a lowercase letter and use camel case. It is possible to introduce other naming rules by changing the \"Pattern\" regular expression. Example: 'val My_Cool_Property = \"\"' The quick-fix renames the class according to the Kotlin naming conventions: 'val myCoolProperty = \"\"'", + "markdown": "Reports property names that do not follow the recommended naming conventions.\n\n\nConsistent naming allows for easier code reading and understanding.\nAccording to the [Kotlin official style guide](https://kotlinlang.org/docs/coding-conventions.html#naming-rules),\nproperty names should start with a lowercase letter and use camel case.\n\nIt is possible to introduce other naming rules by changing the \"Pattern\" regular expression.\n\n**Example:**\n\n\n val My_Cool_Property = \"\"\n\nThe quick-fix renames the class according to the Kotlin naming conventions:\n\n\n val myCoolProperty = \"\"\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "PropertyName", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Naming conventions", + "index": 49, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KotlinMavenPluginPhase", + "shortDescription": { + "text": "Kotlin Maven Plugin misconfigured" + }, + "fullDescription": { + "text": "Reports kotlin-maven-plugin configuration issues", + "markdown": "Reports kotlin-maven-plugin configuration issues" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "KotlinMavenPluginPhase", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin", + "index": 3, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AmbiguousNonLocalJump", + "shortDescription": { + "text": "Ambiguous non-local 'break' or 'continue'" + }, + "fullDescription": { + "text": "Reports 'break' or 'continue' usages inside of lambdas of loop-like functions. 'break' and 'continue' keywords always apply to the real loops ('for', 'while', 'do-while'). 'break' and 'continue' never apply to any function; for example, 'break' and 'continue' don't apply to 'forEach', 'filter', 'map'. Using 'break' or 'continue' inside a loop-like function (for example, 'forEach') may be confusing. The inspection suggests adding a label to clarify to which statement 'break' or 'continue' applies to. Since Kotlin doesn't have a concept of loop-like functions, the inspection uses the heuristic. It assumes that functions that don't have one of 'callsInPlace(EXACTLY_ONCE)' or 'callsInPlace(AT_LEAST_ONCE)' contracts are loop-like functions. Example: 'for (file in files) {\n file.readLines().forEach { line ->\n if (line == commentMarkerLine) continue\n println(line)\n }\n }' The quick-fix adds clarifying labels: 'loop@ for (file in files) {\n file.readLines().forEach { line ->\n if (line == commentMarkerLine) continue@loop\n println(line)\n }\n }'", + "markdown": "Reports `break` or `continue` usages inside of lambdas of loop-like functions.\n\n\n`break` and `continue` keywords always apply to the real loops (`for`,\n`while`, `do-while`). `break` and `continue` never apply to any function; for example,\n`break` and `continue` don't apply to `forEach`, `filter`, `map`.\n\n\nUsing `break` or `continue` inside a loop-like function (for example, `forEach`) may be confusing.\nThe inspection suggests adding a label to clarify to which statement `break` or `continue` applies to.\n\n\nSince Kotlin doesn't have a concept of loop-like functions, the inspection uses the heuristic. It assumes that functions that don't\nhave one of `callsInPlace(EXACTLY_ONCE)` or `callsInPlace(AT_LEAST_ONCE)` contracts are loop-like functions.\n\n**Example:**\n\n\n for (file in files) {\n file.readLines().forEach { line ->\n if (line == commentMarkerLine) continue\n println(line)\n }\n }\n\nThe quick-fix adds clarifying labels:\n\n\n loop@ for (file in files) {\n file.readLines().forEach { line ->\n if (line == commentMarkerLine) continue@loop\n println(line)\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "note", + "parameters": { + "suppressToolId": "AmbiguousNonLocalJump", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceWithStringBuilderAppendRange", + "shortDescription": { + "text": "'StringBuilder.append(CharArray, offset, len)' call on the JVM" + }, + "fullDescription": { + "text": "Reports a 'StringBuilder.append(CharArray, offset, len)' function call on the JVM platform that should be replaced with a 'StringBuilder.appendRange(CharArray, startIndex, endIndex)' function call. The 'append' function behaves differently on the JVM, JS and Native platforms, so using the 'appendRange' function is recommended. Example: 'fun f(charArray: CharArray, offset: Int, len: Int): String {\n return buildString {\n append(charArray, offset, len)\n }\n }' After the quick-fix is applied: 'fun f(charArray: CharArray, offset: Int, len: Int): String {\n return buildString {\n appendRange(charArray, offset, offset + len)\n }\n }'", + "markdown": "Reports a `StringBuilder.append(CharArray, offset, len)` function call on the JVM platform that should be replaced with a `StringBuilder.appendRange(CharArray, startIndex, endIndex)` function call.\n\nThe `append` function behaves differently on the JVM, JS and Native platforms, so using the `appendRange` function is recommended.\n\n**Example:**\n\n\n fun f(charArray: CharArray, offset: Int, len: Int): String {\n return buildString {\n append(charArray, offset, len)\n }\n }\n\nAfter the quick-fix is applied:\n\n\n fun f(charArray: CharArray, offset: Int, len: Int): String {\n return buildString {\n appendRange(charArray, offset, offset + len)\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ReplaceWithStringBuilderAppendRange", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Other problems", + "index": 54, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConvertToStringTemplate", + "shortDescription": { + "text": "String concatenation that can be converted to string template" + }, + "fullDescription": { + "text": "Reports string concatenation that can be converted to a string template. Using string templates is recommended as it makes code easier to read. Example: 'fun example() {\n val capitals = mapOf(\"France\" to \"Paris\", \"Spain\" to \"Madrid\")\n for ((country, capital) in capitals) {\n print(capital + \" is a capital of \" + country)\n }\n }' After the quick-fix is applied: 'fun example() {\n val capitals = mapOf(\"France\" to \"Paris\", \"Spain\" to \"Madrid\")\n for ((country, capital) in capitals) {\n print(\"$capital is a capital of $country\")\n }\n }'", + "markdown": "Reports string concatenation that can be converted to a string template.\n\nUsing string templates is recommended as it makes code easier to read.\n\n**Example:**\n\n\n fun example() {\n val capitals = mapOf(\"France\" to \"Paris\", \"Spain\" to \"Madrid\")\n for ((country, capital) in capitals) {\n print(capital + \" is a capital of \" + country)\n }\n }\n\nAfter the quick-fix is applied:\n\n\n fun example() {\n val capitals = mapOf(\"France\" to \"Paris\", \"Spain\" to \"Madrid\")\n for ((country, capital) in capitals) {\n print(\"$capital is a capital of $country\")\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ConvertToStringTemplate", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KotlinInvalidBundleOrProperty", + "shortDescription": { + "text": "Invalid property key" + }, + "fullDescription": { + "text": "Reports unresolved references to '.properties' file keys and resource bundles in Kotlin files.", + "markdown": "Reports unresolved references to `.properties` file keys and resource bundles in Kotlin files." + }, + "defaultConfiguration": { + "enabled": true, + "level": "error", + "parameters": { + "suppressToolId": "InvalidBundleOrProperty", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin", + "index": 3, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UselessCallOnCollection", + "shortDescription": { + "text": "Useless call on collection type" + }, + "fullDescription": { + "text": "Reports 'filter…' calls from the standard library on already filtered collections. Several functions from the standard library such as 'filterNotNull()' or 'filterIsInstance' have sense only when they are called on receivers that have types distinct from the resulting one. Otherwise, such calls can be omitted as the result will be the same. Remove redundant call quick-fix can be used to amend the code automatically. Example: 'fun test(list: List) {\n val x = list.filterNotNull() // quick-fix simplifies to 'list'\n val y = list.filterIsInstance() // quick-fix simplifies to 'list'\n }'", + "markdown": "Reports `filter...` calls from the standard library on already filtered collections.\n\nSeveral functions from the standard library such as `filterNotNull()` or `filterIsInstance`\nhave sense only when they are called on receivers that have types distinct from the resulting one. Otherwise,\nsuch calls can be omitted as the result will be the same.\n\n**Remove redundant call** quick-fix can be used to amend the code automatically.\n\nExample:\n\n\n fun test(list: List) {\n val x = list.filterNotNull() // quick-fix simplifies to 'list'\n val y = list.filterIsInstance() // quick-fix simplifies to 'list'\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "UselessCallOnCollection", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SimplifiableCall", + "shortDescription": { + "text": "Library function call could be simplified" + }, + "fullDescription": { + "text": "Reports library function calls which could be replaced by simplified one. Using corresponding functions makes your code simpler. The quick-fix replaces the function calls with another one. Example: 'fun test(list: List) {\n list.filter { it is String }\n }' After the quick-fix is applied: 'fun test(list: List) {\n list.filterIsInstance()\n }'", + "markdown": "Reports library function calls which could be replaced by simplified one.\n\nUsing corresponding functions makes your code simpler.\n\nThe quick-fix replaces the function calls with another one.\n\n**Example:**\n\n\n fun test(list: List) {\n list.filter { it is String }\n }\n\nAfter the quick-fix is applied:\n\n\n fun test(list: List) {\n list.filterIsInstance()\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "SimplifiableCall", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ObjectLiteralToLambda", + "shortDescription": { + "text": "Object literal can be converted to lambda" + }, + "fullDescription": { + "text": "Reports anonymous object literals implementing a Java interface with a single abstract method that can be converted into a call with a lambda expression. Example: 'class SomeService {\n val threadPool = Executors.newCachedThreadPool()\n \n fun foo() {\n threadPool.submit(object : Runnable {\n override fun run() {\n println(\"hello\")\n }\n })\n }\n}' After the quick-fix is applied: 'fun foo() {\n threadPool.submit { println(\"hello\") }\n }'", + "markdown": "Reports anonymous object literals implementing a Java interface with a single abstract method that can be converted into a call with a lambda expression.\n\n**Example:**\n\n\n class SomeService {\n val threadPool = Executors.newCachedThreadPool()\n \n fun foo() {\n threadPool.submit(object : Runnable {\n override fun run() {\n println(\"hello\")\n }\n })\n }\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo() {\n threadPool.submit { println(\"hello\") }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ObjectLiteralToLambda", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantLambdaOrAnonymousFunction", + "shortDescription": { + "text": "Redundant creation of lambda or anonymous function" + }, + "fullDescription": { + "text": "Reports lambdas or anonymous functions that are created and used immediately. 'fun test() {\n ({ println() })() // redundant\n (fun() { println() })() // redundant\n }'", + "markdown": "Reports lambdas or anonymous functions that are created and used immediately.\n\n\n fun test() {\n ({ println() })() // redundant\n (fun() { println() })() // redundant\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RedundantLambdaOrAnonymousFunction", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantRequireNotNullCall", + "shortDescription": { + "text": "Redundant 'requireNotNull' or 'checkNotNull' call" + }, + "fullDescription": { + "text": "Reports redundant 'requireNotNull' or 'checkNotNull' call on non-nullable expressions. Example: 'fun foo(i: Int) {\n requireNotNull(i) // This 'i' is always not null, so this 'requireNotNull' call is redundant.\n ...\n }' After the quick-fix is applied: 'fun foo(i: Int) {\n ...\n }'", + "markdown": "Reports redundant `requireNotNull` or `checkNotNull` call on non-nullable expressions.\n\n**Example:**\n\n\n fun foo(i: Int) {\n requireNotNull(i) // This 'i' is always not null, so this 'requireNotNull' call is redundant.\n ...\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(i: Int) {\n ...\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RedundantRequireNotNullCall", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ObjectPropertyName", + "shortDescription": { + "text": "Object property naming convention" + }, + "fullDescription": { + "text": "Reports properties that do not follow the naming conventions. The following properties are reported: Top-level properties Properties in objects and companion objects You can specify the required pattern in the inspection options. Recommended naming conventions: it has to start with an uppercase letter, use camel case and no underscores. Example: '// top-level property\n val USER_NAME_FIELD = \"UserName\"\n // top-level property holding reference to singleton object\n val PersonComparator: Comparator = /*...*/\n\n class Person {\n companion object {\n // property in companion object\n val NO_NAME = Person()\n }\n }'", + "markdown": "Reports properties that do not follow the naming conventions.\n\nThe following properties are reported:\n\n* Top-level properties\n* Properties in objects and companion objects\n\nYou can specify the required pattern in the inspection options.\n\n[Recommended naming conventions](https://kotlinlang.org/docs/coding-conventions.html#naming-rules): it has to start with an uppercase letter, use camel case and no underscores.\n\n**Example:**\n\n\n // top-level property\n val USER_NAME_FIELD = \"UserName\"\n // top-level property holding reference to singleton object\n val PersonComparator: Comparator = /*...*/\n\n class Person {\n companion object {\n // property in companion object\n val NO_NAME = Person()\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ObjectPropertyName", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Naming conventions", + "index": 49, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PackageDirectoryMismatch", + "shortDescription": { + "text": "Package name does not match containing directory" + }, + "fullDescription": { + "text": "Reports 'package' directives that do not match the location of the file. When applying fixes, \"Move refactoring\" defaults are used to update usages of changed declarations, namely: \"Search in comments and strings\" \"Search for text occurrences\"", + "markdown": "Reports `package` directives that do not match the location of the file.\n\n\nWhen applying fixes, \"Move refactoring\" defaults are used to update usages of changed declarations, namely:\n\n* \"Search in comments and strings\"\n* \"Search for text occurrences\"" + }, + "defaultConfiguration": { + "enabled": true, + "level": "note", + "parameters": { + "suppressToolId": "PackageDirectoryMismatch", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Java interop issues", + "index": 70, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConvertObjectToDataObject", + "shortDescription": { + "text": "Convert 'object' to 'data object'" + }, + "fullDescription": { + "text": "Reports 'object' that can be converted to 'data object' 'data object' auto-generates 'toString', 'equals' and 'hashCode' The inspection suggests to convert 'object' to 'data object' in 2 cases: When custom 'toString' returns name of the class When 'object' inherits sealed 'class'/'interface' Example: 'object Foo {\n override fun toString(): String = \"Foo\"\n }' After the quick-fix is applied: 'data object Foo' This inspection only reports if the Kotlin language level of the project or module is 1.9 or higher", + "markdown": "Reports `object` that can be converted to `data object`\n\n`data object` auto-generates `toString`, `equals` and `hashCode`\n\nThe inspection suggests to convert `object` to `data object` in 2 cases:\n\n* When custom `toString` returns name of the class\n* When `object` inherits sealed `class`/`interface`\n\n**Example:**\n\n\n object Foo {\n override fun toString(): String = \"Foo\"\n }\n\nAfter the quick-fix is applied:\n\n\n data object Foo\n\nThis inspection only reports if the Kotlin language level of the project or module is 1.9 or higher" + }, + "defaultConfiguration": { + "enabled": true, + "level": "note", + "parameters": { + "suppressToolId": "ConvertObjectToDataObject", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BooleanLiteralArgument", + "shortDescription": { + "text": "Boolean literal argument without parameter name" + }, + "fullDescription": { + "text": "Reports call arguments with 'Boolean' type without explicit parameter names specified. When multiple boolean literals are passed sequentially, it's easy to forget parameter ordering that could lead to mistakes. Explicit parameter names allow for easier code reading and understanding. Example: 'fun check(checkName: Boolean, checkAddress: Boolean, checkPhone: Boolean) {}\n\n fun usage() {\n check(true, false, true) // What does this mean?\n }' The quick-fix adds missing parameter names: 'fun check(checkName: Boolean, checkAddress: Boolean, checkPhone: Boolean) {}\n\n fun usage() {\n check(checkName = true, checkAddress = false, checkPhone = true)\n }'", + "markdown": "Reports call arguments with `Boolean` type without explicit parameter names specified.\n\n\nWhen multiple boolean literals are passed sequentially, it's easy to forget parameter ordering that could lead to mistakes.\nExplicit parameter names allow for easier code reading and understanding.\n\n**Example:**\n\n\n fun check(checkName: Boolean, checkAddress: Boolean, checkPhone: Boolean) {}\n\n fun usage() {\n check(true, false, true) // What does this mean?\n }\n\nThe quick-fix adds missing parameter names:\n\n\n fun check(checkName: Boolean, checkAddress: Boolean, checkPhone: Boolean) {}\n\n fun usage() {\n check(checkName = true, checkAddress = false, checkPhone = true)\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "BooleanLiteralArgument", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConvertArgumentToSet", + "shortDescription": { + "text": "Argument could be converted to 'Set' to improve performance" + }, + "fullDescription": { + "text": "Detects the function calls that could work faster with an argument converted to 'Set'. Operations like 'minus' or 'intersect' are more effective when their argument is a set. An explicit conversion of an 'Iterable' or an 'Array' into a 'Set' can often make code more effective. The quick-fix adds an explicit conversion to the function call. Example: 'fun f(a: Iterable, b: Iterable): Int =\n a.intersect(b).size' After the quick-fix is applied: 'fun f(a: Iterable, b: Iterable): Int =\n a.intersect(b.toSet()).size'", + "markdown": "Detects the function calls that could work faster with an argument converted to `Set`.\n\n\nOperations like 'minus' or 'intersect' are more effective when their argument is a set.\nAn explicit conversion of an `Iterable` or an `Array`\ninto a `Set` can often make code more effective.\n\n\nThe quick-fix adds an explicit conversion to the function call.\n\n**Example:**\n\n\n fun f(a: Iterable, b: Iterable): Int =\n a.intersect(b).size\n\nAfter the quick-fix is applied:\n\n\n fun f(a: Iterable, b: Iterable): Int =\n a.intersect(b.toSet()).size\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "note", + "parameters": { + "suppressToolId": "ConvertArgumentToSet", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Other problems", + "index": 54, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KotlinCovariantEquals", + "shortDescription": { + "text": "Covariant 'equals()'" + }, + "fullDescription": { + "text": "Reports 'equals()' that takes an argument type other than 'Any?' if the class does not have another 'equals()' that takes 'Any?' as its argument type. Example: 'class Foo {\n fun equals(other: Foo?): Boolean {\n return true\n }\n }' To fix the problem create 'equals()' method that takes an argument of type 'Any?'.", + "markdown": "Reports `equals()` that takes an argument type other than `Any?` if the class does not have another `equals()` that takes `Any?` as its argument type.\n\n**Example:**\n\n\n class Foo {\n fun equals(other: Foo?): Boolean {\n return true\n }\n }\n\nTo fix the problem create `equals()` method that takes an argument of type `Any?`." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "CovariantEquals", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceGuardClauseWithFunctionCall", + "shortDescription": { + "text": "Guard clause can be replaced with Kotlin's function call" + }, + "fullDescription": { + "text": "Reports guard clauses that can be replaced with a function call. Example: 'fun test(foo: Int?) {\n if (foo == null) throw IllegalArgumentException(\"foo\") // replaceable clause\n }' After the quick-fix is applied: 'fun test(foo: Int?) {\n checkNotNull(foo)\n }'", + "markdown": "Reports guard clauses that can be replaced with a function call.\n\n**Example:**\n\n fun test(foo: Int?) {\n if (foo == null) throw IllegalArgumentException(\"foo\") // replaceable clause\n }\n\nAfter the quick-fix is applied:\n\n fun test(foo: Int?) {\n checkNotNull(foo)\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceGuardClauseWithFunctionCall", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceSizeZeroCheckWithIsEmpty", + "shortDescription": { + "text": "Size zero check can be replaced with 'isEmpty()'" + }, + "fullDescription": { + "text": "Reports 'size == 0' checks on 'Collections/Array/String' that should be replaced with 'isEmpty()'. Using 'isEmpty()' makes your code simpler. The quick-fix replaces the size check with 'isEmpty()'. Example: 'fun foo() {\n val arrayOf = arrayOf(1, 2, 3)\n arrayOf.size == 0\n }' After the quick-fix is applied: 'fun foo() {\n val arrayOf = arrayOf(1, 2, 3)\n arrayOf.isEmpty()\n }'", + "markdown": "Reports `size == 0` checks on `Collections/Array/String` that should be replaced with `isEmpty()`.\n\nUsing `isEmpty()` makes your code simpler.\n\nThe quick-fix replaces the size check with `isEmpty()`.\n\n**Example:**\n\n\n fun foo() {\n val arrayOf = arrayOf(1, 2, 3)\n arrayOf.size == 0\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo() {\n val arrayOf = arrayOf(1, 2, 3)\n arrayOf.isEmpty()\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceSizeZeroCheckWithIsEmpty", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AmbiguousExpressionInWhenBranchMigration", + "shortDescription": { + "text": "Ambiguous logical expressions in 'when' branches since 1.7" + }, + "fullDescription": { + "text": "Reports ambiguous logical expressions in 'when' branches which cause compilation errors in Kotlin 1.8 and later. 'fun Int.matches(strict: Boolean): Boolean = when (strict) {\n true -> this == 6\n this in (4..7) -> true // is ambiguous\n else -> false\n }' After the quick-fix is applied: 'fun Int.matches(strict: Boolean): Boolean = when (strict) {\n true -> this == 6\n (this in (4..7)) -> true // wrapped in parentheses\n else -> false\n }' Inspection is available for Kotlin language level starting from 1.7.", + "markdown": "Reports ambiguous logical expressions in `when` branches which cause compilation errors in Kotlin 1.8 and later.\n\n\n fun Int.matches(strict: Boolean): Boolean = when (strict) {\n true -> this == 6\n this in (4..7) -> true // is ambiguous\n else -> false\n }\n\nAfter the quick-fix is applied:\n\n\n fun Int.matches(strict: Boolean): Boolean = when (strict) {\n true -> this == 6\n (this in (4..7)) -> true // wrapped in parentheses\n else -> false\n }\n\nInspection is available for Kotlin language level starting from 1.7." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AmbiguousExpressionInWhenBranchMigration", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Migration", + "index": 16, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantEnumConstructorInvocation", + "shortDescription": { + "text": "Redundant enum constructor invocation" + }, + "fullDescription": { + "text": "Reports redundant constructor invocation on an enum entry. Example: 'enum class Baz(i: Int = 0) {\n A(1),\n B(),\n C(),\n }' After the quick-fix is applied: 'enum class Baz(i: Int = 0) {\n A(1),\n B,\n C,\n }'", + "markdown": "Reports redundant constructor invocation on an enum entry.\n\n**Example:**\n\n\n enum class Baz(i: Int = 0) {\n A(1),\n B(),\n C(),\n }\n\nAfter the quick-fix is applied:\n\n\n enum class Baz(i: Int = 0) {\n A(1),\n B,\n C,\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RedundantEnumConstructorInvocation", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceToStringWithStringTemplate", + "shortDescription": { + "text": "Call of 'toString' could be replaced with string template" + }, + "fullDescription": { + "text": "Reports 'toString' function calls that can be replaced with a string template. Using string templates makes your code simpler. The quick-fix replaces 'toString' with a string template. Example: 'fun test(): String {\n val x = 1\n return x.toString()\n }' After the quick-fix is applied: 'fun test(): String {\n val x = 1\n return \"$x\"\n }'", + "markdown": "Reports `toString` function calls that can be replaced with a string template.\n\nUsing string templates makes your code simpler.\n\nThe quick-fix replaces `toString` with a string template.\n\n**Example:**\n\n\n fun test(): String {\n val x = 1\n return x.toString()\n }\n\nAfter the quick-fix is applied:\n\n\n fun test(): String {\n val x = 1\n return \"$x\"\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceToStringWithStringTemplate", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ProtectedInFinal", + "shortDescription": { + "text": "'protected' visibility is effectively 'private' in a final class" + }, + "fullDescription": { + "text": "Reports 'protected' visibility used inside of a 'final' class. In such cases 'protected' members are accessible only in the class itself, so they are effectively 'private'. Example: 'class FinalClass {\n protected fun foo() {}\n }' After the quick-fix is applied: 'class FinalClass {\n private fun foo() {}\n }'", + "markdown": "Reports `protected` visibility used inside of a `final` class. In such cases `protected` members are accessible only in the class itself, so they are effectively `private`.\n\n**Example:**\n\n\n class FinalClass {\n protected fun foo() {}\n }\n\nAfter the quick-fix is applied:\n\n\n class FinalClass {\n private fun foo() {}\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ProtectedInFinal", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FakeJvmFieldConstant", + "shortDescription": { + "text": "Kotlin non-const property used as Java constant" + }, + "fullDescription": { + "text": "Reports Kotlin properties that are not 'const' and used as Java annotation arguments. For example, a property with the '@JvmField' annotation has an initializer that can be evaluated at compile-time, and it has a primitive or 'String' type. Such properties have a 'ConstantValue' attribute in bytecode in Kotlin 1.1-1.2. This attribute allows javac to fold usages of the corresponding field and use that field in annotations. This can lead to incorrect behavior in the case of separate or incremental compilation in mixed Java/Kotlin code. This behavior is subject to change in Kotlin 1.3 (no 'ConstantValue' attribute any more). Example: Kotlin code in foo.kt file: 'annotation class Ann(val s: String)\n @JvmField val importantString = \"important\"' Java code: 'public class JavaUser {\n // This is dangerous\n @Ann(s = FooKt.importantString)\n public void foo() {}\n }' To fix the problem replace the '@JvmField' annotation with the 'const' modifier on a relevant Kotlin property or inline it.", + "markdown": "Reports Kotlin properties that are not `const` and used as Java annotation arguments.\n\n\nFor example, a property with the `@JvmField` annotation has an initializer that can be evaluated at compile-time,\nand it has a primitive or `String` type.\n\n\nSuch properties have a `ConstantValue` attribute in bytecode in Kotlin 1.1-1.2.\nThis attribute allows javac to fold usages of the corresponding field and use that field in annotations.\nThis can lead to incorrect behavior in the case of separate or incremental compilation in mixed Java/Kotlin code.\nThis behavior is subject to change in Kotlin 1.3 (no `ConstantValue` attribute any more).\n\n**Example:**\n\nKotlin code in foo.kt file:\n\n\n annotation class Ann(val s: String)\n @JvmField val importantString = \"important\"\n\nJava code:\n\n\n public class JavaUser {\n // This is dangerous\n @Ann(s = FooKt.importantString)\n public void foo() {}\n }\n\nTo fix the problem replace the `@JvmField` annotation with the `const` modifier on a relevant Kotlin property or inline it." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "FakeJvmFieldConstant", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Java interop issues", + "index": 70, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "WhenWithOnlyElse", + "shortDescription": { + "text": "'when' has only 'else' branch and can be simplified" + }, + "fullDescription": { + "text": "Reports 'when' expressions with only an 'else' branch that can be simplified. Simplify expression quick-fix can be used to amend the code automatically. Example: 'fun redundant() {\n val x = when { // <== redundant, the quick-fix simplifies the when expression to \"val x = 1\"\n else -> 1\n }\n }'", + "markdown": "Reports `when` expressions with only an `else` branch that can be simplified.\n\n**Simplify expression** quick-fix can be used to amend the code automatically.\n\nExample:\n\n\n fun redundant() {\n val x = when { // <== redundant, the quick-fix simplifies the when expression to \"val x = 1\"\n else -> 1\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "WhenWithOnlyElse", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceRangeStartEndInclusiveWithFirstLast", + "shortDescription": { + "text": "Boxed properties should be replaced with unboxed" + }, + "fullDescription": { + "text": "Reports boxed 'Range.start' and 'Range.endInclusive' properties. These properties can be replaced with unboxed 'first' and 'last' properties to avoid redundant calls. The quick-fix replaces 'start' and 'endInclusive' properties with the corresponding 'first' and 'last'. Example: 'fun foo(range: CharRange) {\n val lastElement = range.endInclusive\n }' After the quick-fix is applied: 'fun foo(range: CharRange) {\n val lastElement = range.last\n }'", + "markdown": "Reports **boxed** `Range.start` and `Range.endInclusive` properties.\n\nThese properties can be replaced with **unboxed** `first` and `last` properties to avoid redundant calls.\n\nThe quick-fix replaces `start` and `endInclusive` properties with the corresponding `first` and `last`.\n\n**Example:**\n\n\n fun foo(range: CharRange) {\n val lastElement = range.endInclusive\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(range: CharRange) {\n val lastElement = range.last\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceRangeStartEndInclusiveWithFirstLast", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceSizeCheckWithIsNotEmpty", + "shortDescription": { + "text": "Size check can be replaced with 'isNotEmpty()'" + }, + "fullDescription": { + "text": "Reports size checks of 'Collections/Array/String' that should be replaced with 'isNotEmpty()'. Using 'isNotEmpty()' makes your code simpler. The quick-fix replaces the size check with 'isNotEmpty()'. Example: 'fun foo() {\n val arrayOf = arrayOf(1, 2, 3)\n arrayOf.size > 0\n }' After the quick-fix is applied: 'fun foo() {\n val arrayOf = arrayOf(1, 2, 3)\n arrayOf.isNotEmpty()\n }'", + "markdown": "Reports size checks of `Collections/Array/String` that should be replaced with `isNotEmpty()`.\n\nUsing `isNotEmpty()` makes your code simpler.\n\nThe quick-fix replaces the size check with `isNotEmpty()`.\n\n**Example:**\n\n\n fun foo() {\n val arrayOf = arrayOf(1, 2, 3)\n arrayOf.size > 0\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo() {\n val arrayOf = arrayOf(1, 2, 3)\n arrayOf.isNotEmpty()\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceSizeCheckWithIsNotEmpty", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantSemicolon", + "shortDescription": { + "text": "Redundant semicolon" + }, + "fullDescription": { + "text": "Reports redundant semicolons (';') that can be safely removed. Kotlin does not require a semicolon at the end of each statement or expression. The quick-fix is suggested to remove redundant semicolons. Example: 'val myMap = mapOf(\"one\" to 1, \"two\" to 2);\n myMap.forEach { (key, value) -> print(\"$key -> $value\")};' After the quick-fix is applied: 'val myMap = mapOf(\"one\" to 1, \"two\" to 2)\n myMap.forEach { (key, value) -> print(\"$key -> $value\")}' There are two cases though where a semicolon is required: Several statements placed on a single line need to be separated with semicolons: 'map.forEach { val (key, value) = it; println(\"$key -> $value\") }' 'enum' classes that also declare properties or functions, require a semicolon after the list of enum constants: 'enum class Mode {\n SILENT, VERBOSE;\n\n fun isSilent(): Boolean = this == SILENT\n }'", + "markdown": "Reports redundant semicolons (`;`) that can be safely removed.\n\n\nKotlin does not require a semicolon at the end of each statement or expression.\nThe quick-fix is suggested to remove redundant semicolons.\n\n**Example:**\n\n\n val myMap = mapOf(\"one\" to 1, \"two\" to 2);\n myMap.forEach { (key, value) -> print(\"$key -> $value\")};\n\nAfter the quick-fix is applied:\n\n\n val myMap = mapOf(\"one\" to 1, \"two\" to 2)\n myMap.forEach { (key, value) -> print(\"$key -> $value\")}\n\nThere are two cases though where a semicolon is required:\n\n1. Several statements placed on a single line need to be separated with semicolons:\n\n\n map.forEach { val (key, value) = it; println(\"$key -> $value\") }\n\n2. `enum` classes that also declare properties or functions, require a semicolon after the list of enum constants:\n\n\n enum class Mode {\n SILENT, VERBOSE;\n\n fun isSilent(): Boolean = this == SILENT\n }\n \n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantSemicolon", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IntroduceWhenSubject", + "shortDescription": { + "text": "'when' that can be simplified by introducing an argument" + }, + "fullDescription": { + "text": "Reports a 'when' expression that can be simplified by introducing a subject argument. Example: 'fun test(obj: Any): String {\n return when {\n obj is String -> \"string\"\n obj is Int -> \"int\"\n else -> \"unknown\"\n }\n }' The quick fix introduces a subject argument: 'fun test(obj: Any): String {\n return when (obj) {\n is String -> \"string\"\n is Int -> \"int\"\n else -> \"unknown\"\n }\n }'", + "markdown": "Reports a `when` expression that can be simplified by introducing a subject argument.\n\n**Example:**\n\n\n fun test(obj: Any): String {\n return when {\n obj is String -> \"string\"\n obj is Int -> \"int\"\n else -> \"unknown\"\n }\n }\n\nThe quick fix introduces a subject argument:\n\n\n fun test(obj: Any): String {\n return when (obj) {\n is String -> \"string\"\n is Int -> \"int\"\n else -> \"unknown\"\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "IntroduceWhenSubject", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KotlinTestJUnit", + "shortDescription": { + "text": "kotlin-test-junit could be used" + }, + "fullDescription": { + "text": "Reports usage of 'kotlin-test' and 'junit' dependency without 'kotlin-test-junit'. It is recommended to use 'kotlin-test-junit' dependency to work with Kotlin and JUnit.", + "markdown": "Reports usage of `kotlin-test` and `junit` dependency without `kotlin-test-junit`.\n\nIt is recommended to use `kotlin-test-junit` dependency to work with Kotlin and JUnit." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "KotlinTestJUnit", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin", + "index": 3, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SafeCastWithReturn", + "shortDescription": { + "text": "Safe cast with 'return' should be replaced with 'if' type check" + }, + "fullDescription": { + "text": "Reports safe cast with 'return' that can be replaced with 'if' type check. Using corresponding functions makes your code simpler. The quick-fix replaces the safe cast with 'if' type check. Example: 'fun test(x: Any) {\n x as? String ?: return\n }' After the quick-fix is applied: 'fun test(x: Any) {\n if (x !is String) return\n }'", + "markdown": "Reports safe cast with `return` that can be replaced with `if` type check.\n\nUsing corresponding functions makes your code simpler.\n\nThe quick-fix replaces the safe cast with `if` type check.\n\n**Example:**\n\n\n fun test(x: Any) {\n x as? String ?: return\n }\n\nAfter the quick-fix is applied:\n\n\n fun test(x: Any) {\n if (x !is String) return\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "SafeCastWithReturn", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceAssertBooleanWithAssertEquality", + "shortDescription": { + "text": "Assert boolean could be replaced with assert equality" + }, + "fullDescription": { + "text": "Reports calls to 'assertTrue()' and 'assertFalse()' that can be replaced with assert equality functions. 'assertEquals()', 'assertSame()', and their negating counterparts (-Not-) provide more informative messages on failure. Example: 'assertTrue(a == b)' After the quick-fix is applied: 'assertEquals(a, b)'", + "markdown": "Reports calls to `assertTrue()` and `assertFalse()` that can be replaced with assert equality functions.\n\n\n`assertEquals()`, `assertSame()`, and their negating counterparts (-Not-) provide more informative messages on\nfailure.\n\n**Example:**\n\n assertTrue(a == b)\n\nAfter the quick-fix is applied:\n\n assertEquals(a, b)\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceAssertBooleanWithAssertEquality", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DeprecatedCallableAddReplaceWith", + "shortDescription": { + "text": "@Deprecated annotation without 'replaceWith' argument" + }, + "fullDescription": { + "text": "Reports deprecated functions and properties that do not have the 'kotlin.ReplaceWith' argument in its 'kotlin.deprecated' annotation and suggests to add one based on their body. Kotlin provides the 'ReplaceWith' argument to replace deprecated declarations automatically. It is recommended to use the argument to fix deprecation issues in code. Example: '@Deprecated(\"Use refined() instead.\")\n fun deprecated() = refined()\n\n fun refined() = 42' The quick-fix adds the 'ReplaceWith()' argument: '@Deprecated(\"Use refined() instead.\", ReplaceWith(\"refined()\"))\n fun deprecated() = refined()\n\n fun refined() = 42'", + "markdown": "Reports deprecated functions and properties that do not have the `kotlin.ReplaceWith` argument in its `kotlin.deprecated` annotation and suggests to add one based on their body.\n\n\nKotlin provides the `ReplaceWith` argument to replace deprecated declarations automatically.\nIt is recommended to use the argument to fix deprecation issues in code.\n\n**Example:**\n\n\n @Deprecated(\"Use refined() instead.\")\n fun deprecated() = refined()\n\n fun refined() = 42\n\nThe quick-fix adds the `ReplaceWith()` argument:\n\n\n @Deprecated(\"Use refined() instead.\", ReplaceWith(\"refined()\"))\n fun deprecated() = refined()\n\n fun refined() = 42\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "note", + "parameters": { + "suppressToolId": "DeprecatedCallableAddReplaceWith", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Other problems", + "index": 54, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryOptInAnnotation", + "shortDescription": { + "text": "Unnecessary '@OptIn' annotation" + }, + "fullDescription": { + "text": "Reports unnecessary opt-in annotations that can be safely removed. '@OptIn' annotation is required for the code using experimental APIs that can change any time in the future. This annotation becomes useless and possibly misleading if no such API is used (e.g., when the experimental API becomes stable and does not require opting in its usage anymore). Remove annotation quick-fix can be used to remove the unnecessary '@OptIn' annotation. Example: '@OptIn(ExperimentalApi::class)\n fun foo(x: Bar) {\n x.baz()\n }' After the quick-fix is applied: 'fun foo(x: Bar) {\n x.baz()\n }'", + "markdown": "Reports unnecessary opt-in annotations that can be safely removed.\n\n`@OptIn` annotation is required for the code using experimental APIs that can change\nany time in the future. This annotation becomes useless and possibly misleading if no such API is used\n(e.g., when the experimental API becomes stable and does not require opting in its usage anymore).\n\n\n**Remove annotation** quick-fix can be used to remove the unnecessary `@OptIn` annotation.\n\nExample:\n\n\n @OptIn(ExperimentalApi::class)\n fun foo(x: Bar) {\n x.baz()\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(x: Bar) {\n x.baz()\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "UnnecessaryOptInAnnotation", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ComplexRedundantLet", + "shortDescription": { + "text": "Redundant argument-based 'let' call" + }, + "fullDescription": { + "text": "Reports a redundant argument-based 'let' call. 'let' is redundant when the lambda parameter is only used as a qualifier in a call expression. If you need to give a name to the qualifying expression, declare a local variable. Example: 'fun splitNumbers() {\n \"1,2,3\".let { it.split(',') }\n }' The quick-fix removes the extra 'let()' call: 'fun example() {\n \"1,2,3\".split(',')\n }' Alternative: 'fun splitNumbers() {\n val numbers = \"1,2,3\"\n numbers.split(',')\n }'", + "markdown": "Reports a redundant argument-based `let` call.\n\n`let` is redundant when the lambda parameter is only used as a qualifier in a call expression.\n\nIf you need to give a name to the qualifying expression, declare a local variable.\n\n**Example:**\n\n\n fun splitNumbers() {\n \"1,2,3\".let { it.split(',') }\n }\n\nThe quick-fix removes the extra `let()` call:\n\n\n fun example() {\n \"1,2,3\".split(',')\n }\n\nAlternative:\n\n\n fun splitNumbers() {\n val numbers = \"1,2,3\"\n numbers.split(',')\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ComplexRedundantLet", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RemoveRedundantSpreadOperator", + "shortDescription": { + "text": "Redundant spread operator" + }, + "fullDescription": { + "text": "Reports the use of a redundant spread operator for a family of 'arrayOf' function calls. Use the 'Remove redundant spread operator' quick-fix to clean up the code. Examples: 'fun foo(vararg s: String) { }\n\n fun bar(ss: Array) {\n foo(*arrayOf(\"abc\")) // for the both calls of 'foo', array creation\n foo(*arrayOf(*ss, \"zzz\")) // and its subsequent \"spreading\" is redundant\n }' After the quick-fix is applied: 'fun foo(vararg s: String) { }\n\n fun bar(ss: Array) {\n foo(\"abc\")\n foo(*ss, \"zzz\")\n }'", + "markdown": "Reports the use of a redundant spread operator for a family of `arrayOf` function calls.\n\nUse the 'Remove redundant spread operator' quick-fix to clean up the code.\n\n**Examples:**\n\n\n fun foo(vararg s: String) { }\n\n fun bar(ss: Array) {\n foo(*arrayOf(\"abc\")) // for the both calls of 'foo', array creation\n foo(*arrayOf(*ss, \"zzz\")) // and its subsequent \"spreading\" is redundant\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(vararg s: String) { }\n\n fun bar(ss: Array) {\n foo(\"abc\")\n foo(*ss, \"zzz\")\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RemoveRedundantSpreadOperator", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonVarPropertyInExternalInterface", + "shortDescription": { + "text": "External interface contains val property" + }, + "fullDescription": { + "text": "Reports not var properties in external interface. Read more in the migration guide.", + "markdown": "Reports not var properties in external interface. Read more in the [migration guide](https://kotlinlang.org/docs/js-ir-migration.html#convert-properties-of-external-interfaces-to-var)." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "NonVarPropertyInExternalInterface", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ProhibitJvmOverloadsOnConstructorsOfAnnotationClassesMigration", + "shortDescription": { + "text": "'@JvmOverloads' annotation cannot be used on constructors of annotation classes since 1.4" + }, + "fullDescription": { + "text": "Reports '@JvmOverloads' on constructors of annotation classes because it's meaningless. There is no footprint of '@JvmOverloads' in the generated bytecode and Kotlin metadata, so '@JvmOverloads' doesn't affect the generated bytecode and the code behavior. '@JvmOverloads' on constructors of annotation classes causes a compilation error since Kotlin 1.4. Example: 'annotation class A @JvmOverloads constructor(val x: Int = 1)' After the quick-fix is applied: 'annotation class A constructor(val x: Int = 1)'", + "markdown": "Reports `@JvmOverloads` on constructors of annotation classes because it's meaningless.\n\n\nThere is no footprint of `@JvmOverloads` in the generated bytecode and Kotlin metadata,\nso `@JvmOverloads` doesn't affect the generated bytecode and the code behavior.\n\n`@JvmOverloads` on constructors of annotation classes causes a compilation error since Kotlin 1.4.\n\n**Example:**\n\n\n annotation class A @JvmOverloads constructor(val x: Int = 1)\n\nAfter the quick-fix is applied:\n\n\n annotation class A constructor(val x: Int = 1)\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "ProhibitJvmOverloadsOnConstructorsOfAnnotationClassesMigration", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Migration", + "index": 16, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RemoveSetterParameterType", + "shortDescription": { + "text": "Redundant setter parameter type" + }, + "fullDescription": { + "text": "Reports explicitly specified parameter types in property setters. Setter parameter type always matches the property type, so it's not required to be explicit. The 'Remove explicit type specification' quick-fix allows amending the code accordingly. Examples: 'fun process(x: Int) {}\n\n var x: Int = 0\n set(value: Int) = process(value) // <== 'Int' specification can be safely omitted' After the quick-fix is applied: 'fun process(x: Int) {}\n\n var x: Int = 0\n set(value) = process(value)'", + "markdown": "Reports explicitly specified parameter types in property setters.\n\n\nSetter parameter type always matches the property type, so it's not required to be explicit.\nThe 'Remove explicit type specification' quick-fix allows amending the code accordingly.\n\n**Examples:**\n\n\n fun process(x: Int) {}\n\n var x: Int = 0\n set(value: Int) = process(value) // <== 'Int' specification can be safely omitted\n\nAfter the quick-fix is applied:\n\n\n fun process(x: Int) {}\n\n var x: Int = 0\n set(value) = process(value)\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RemoveSetterParameterType", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IfThenToElvis", + "shortDescription": { + "text": "If-Then foldable to '?:'" + }, + "fullDescription": { + "text": "Reports 'if-then' expressions that can be folded into elvis ('?:') expressions. Example: 'fun maybeFoo(): String? = \"foo\"\n\n var foo = maybeFoo()\n val bar = if (foo == null) \"hello\" else foo' The quick fix converts the 'if-then' expression into an elvis ('?:') expression: 'fun maybeFoo(): String? = \"foo\"\n\n var foo = maybeFoo()\n val bar = foo ?: \"hello\"'", + "markdown": "Reports `if-then` expressions that can be folded into elvis (`?:`) expressions.\n\n**Example:**\n\n\n fun maybeFoo(): String? = \"foo\"\n\n var foo = maybeFoo()\n val bar = if (foo == null) \"hello\" else foo\n\nThe quick fix converts the `if-then` expression into an elvis (`?:`) expression:\n\n\n fun maybeFoo(): String? = \"foo\"\n\n var foo = maybeFoo()\n val bar = foo ?: \"hello\"\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "IfThenToElvis", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ObjectPrivatePropertyName", + "shortDescription": { + "text": "Object private property naming convention" + }, + "fullDescription": { + "text": "Reports properties that do not follow the naming conventions. The following properties are reported: Private properties in objects and companion objects You can specify the required pattern in the inspection options. Recommended naming conventions: it has to start with an underscore or an uppercase letter, use camel case. Example: 'class Person {\n companion object {\n // property in companion object\n private val NO_NAME = Person()\n }\n }'", + "markdown": "Reports properties that do not follow the naming conventions.\n\nThe following properties are reported:\n\n* Private properties in objects and companion objects\n\nYou can specify the required pattern in the inspection options.\n\n[Recommended naming conventions](https://kotlinlang.org/docs/coding-conventions.html#naming-rules): it has to start with an underscore or an uppercase letter, use camel case.\n\n**Example:**\n\n\n class Person {\n companion object {\n // property in companion object\n private val NO_NAME = Person()\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ObjectPrivatePropertyName", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Naming conventions", + "index": 49, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "WrapUnaryOperator", + "shortDescription": { + "text": "Ambiguous unary operator use with number constant" + }, + "fullDescription": { + "text": "Reports an unary operator followed by a dot qualifier such as '-1.inc()'. Code like '-1.inc()' can be misleading because '-' has a lower precedence than '.inc()'. As a result, '-1.inc()' evaluates to '-2' and not '0' as it might be expected. Wrap unary operator and value with () quick-fix can be used to amend the code automatically.", + "markdown": "Reports an unary operator followed by a dot qualifier such as `-1.inc()`.\n\nCode like `-1.inc()` can be misleading because `-` has a lower precedence than `.inc()`.\nAs a result, `-1.inc()` evaluates to `-2` and not `0` as it might be expected.\n\n**Wrap unary operator and value with ()** quick-fix can be used to amend the code automatically." + }, + "defaultConfiguration": { + "enabled": true, + "level": "note", + "parameters": { + "suppressToolId": "WrapUnaryOperator", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConflictingExtensionProperty", + "shortDescription": { + "text": "Extension property conflicting with synthetic one" + }, + "fullDescription": { + "text": "Reports extension properties that conflict with synthetic ones that have been automatically produced from Java 'get' or 'set' methods. Such properties should be either removed or renamed to avoid breaking code by future changes in the compiler. The quick-fix deletes an extention property. Example: 'val File.name: String\n get() = getName()' The quick-fix adds the '@Deprecated' annotation: '@Deprecated(\"Is replaced with automatic synthetic extension\", ReplaceWith(\"name\"), level = DeprecationLevel.HIDDEN)\n val File.name: String\n get() = getName()'", + "markdown": "Reports extension properties that conflict with synthetic ones that have been automatically produced from Java `get` or `set` methods.\n\nSuch properties should be either removed or renamed to avoid breaking code by future changes in the compiler.\n\nThe quick-fix deletes an extention property.\n\n**Example:**\n\n\n val File.name: String\n get() = getName()\n\nThe quick-fix adds the `@Deprecated` annotation:\n\n\n @Deprecated(\"Is replaced with automatic synthetic extension\", ReplaceWith(\"name\"), level = DeprecationLevel.HIDDEN)\n val File.name: String\n get() = getName()\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ConflictingExtensionProperty", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceStringFormatWithLiteral", + "shortDescription": { + "text": "'String.format' call can be replaced with string templates" + }, + "fullDescription": { + "text": "Reports 'String.format' calls that can be replaced with string templates. Using string templates makes your code simpler. The quick-fix replaces the call with a string template. Example: 'fun main() {\n val id = \"abc\"\n val date = \"123\"\n val s = String.format(\"%s_%s_%s\", id, date, id)\n }' After the quick-fix is applied: 'fun main() {\n val id = \"abc\"\n val date = \"123\"\n val s = \"${id}_${date}_$id\"\n }'", + "markdown": "Reports `String.format` calls that can be replaced with string templates.\n\nUsing string templates makes your code simpler.\n\nThe quick-fix replaces the call with a string template.\n\n**Example:**\n\n\n fun main() {\n val id = \"abc\"\n val date = \"123\"\n val s = String.format(\"%s_%s_%s\", id, date, id)\n }\n\nAfter the quick-fix is applied:\n\n\n fun main() {\n val id = \"abc\"\n val date = \"123\"\n val s = \"${id}_${date}_$id\"\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceStringFormatWithLiteral", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceNotNullAssertionWithElvisReturn", + "shortDescription": { + "text": "Not-null assertion can be replaced with 'return'" + }, + "fullDescription": { + "text": "Reports not-null assertion ('!!') calls that can be replaced with the elvis operator and return ('?: return'). A not-null assertion can lead to NPE (NullPointerException) that is not expected. Avoiding the use of '!!' is good practice. The quick-fix replaces the not-null assertion with 'return' or 'return null'. Example: 'fun test(number: Int?) {\n val x = number!!\n }' After the quick-fix is applied: 'fun test(number: Int?) {\n val x = number ?: return\n }'", + "markdown": "Reports not-null assertion (`!!`) calls that can be replaced with the elvis operator and return (`?: return`).\n\nA not-null assertion can lead to NPE (NullPointerException) that is not expected. Avoiding the use of `!!` is good practice.\n\nThe quick-fix replaces the not-null assertion with `return` or `return null`.\n\n**Example:**\n\n\n fun test(number: Int?) {\n val x = number!!\n }\n\nAfter the quick-fix is applied:\n\n\n fun test(number: Int?) {\n val x = number ?: return\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceNotNullAssertionWithElvisReturn", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceSubstringWithSubstringBefore", + "shortDescription": { + "text": "'substring' call should be replaced with 'substringBefore'" + }, + "fullDescription": { + "text": "Reports calls like 's.substring(0, s.indexOf(x))' that can be replaced with 's.substringBefore(x)'. Using 'substringBefore()' makes your code simpler. The quick-fix replaces the 'substring' call with 'substringBefore'. Example: 'fun foo(s: String) {\n s.substring(0, s.indexOf('x'))\n }' After the quick-fix is applied: 'fun foo(s: String) {\n s.substringBefore('x')\n }'", + "markdown": "Reports calls like `s.substring(0, s.indexOf(x))` that can be replaced with `s.substringBefore(x)`.\n\nUsing `substringBefore()` makes your code simpler.\n\nThe quick-fix replaces the `substring` call with `substringBefore`.\n\n**Example:**\n\n\n fun foo(s: String) {\n s.substring(0, s.indexOf('x'))\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(s: String) {\n s.substringBefore('x')\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceSubstringWithSubstringBefore", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceJavaStaticMethodWithKotlinAnalog", + "shortDescription": { + "text": "Java methods should be replaced with Kotlin analog" + }, + "fullDescription": { + "text": "Reports a Java method call that can be replaced with a Kotlin function, for example, 'System.out.println()'. Replacing the code gets rid of the dependency to Java and makes the idiomatic Kotlin code. The quick-fix replaces the Java method calls on the same Kotlin call. Example: 'import java.util.Arrays\n\n fun main() {\n val a = Arrays.asList(1, 3, null)\n }' After the quick-fix is applied: 'fun main() {\n val a = listOf(1, 3, null)\n }'", + "markdown": "Reports a Java method call that can be replaced with a Kotlin function, for example, `System.out.println()`.\n\nReplacing the code gets rid of the dependency to Java and makes the idiomatic Kotlin code.\n\nThe quick-fix replaces the Java method calls on the same Kotlin call.\n\n**Example:**\n\n\n import java.util.Arrays\n\n fun main() {\n val a = Arrays.asList(1, 3, null)\n }\n\nAfter the quick-fix is applied:\n\n\n fun main() {\n val a = listOf(1, 3, null)\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceJavaStaticMethodWithKotlinAnalog", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RemoveEmptyParenthesesFromLambdaCall", + "shortDescription": { + "text": "Unnecessary parentheses in function call with lambda" + }, + "fullDescription": { + "text": "Reports redundant empty parentheses of function calls where the only parameter is a lambda that's outside the parentheses. Use the 'Remove unnecessary parentheses from function call with lambda' quick-fix to clean up the code. Examples: 'fun foo() {\n listOf(1).forEach() { }\n }' After the quick-fix is applied: 'fun foo() {\n listOf(1).forEach { }\n }'", + "markdown": "Reports redundant empty parentheses of function calls where the only parameter is a lambda that's outside the parentheses.\n\nUse the 'Remove unnecessary parentheses from function call with lambda' quick-fix to clean up the code.\n\n**Examples:**\n\n\n fun foo() {\n listOf(1).forEach() { }\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo() {\n listOf(1).forEach { }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RemoveEmptyParenthesesFromLambdaCall", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceWithOperatorAssignment", + "shortDescription": { + "text": "Assignment can be replaced with operator assignment" + }, + "fullDescription": { + "text": "Reports modifications of variables with a simple assignment (such as 'y = y + x') that can be replaced with an operator assignment. The quick-fix replaces the assignment with an assignment operator. Example: 'fun foo() {\n val list = mutableListOf(1, 2, 3)\n list = list + 4\n }' After the quick-fix is applied: 'fun foo() {\n val list = mutableListOf(1, 2, 3)\n list += 4\n }'", + "markdown": "Reports modifications of variables with a simple assignment (such as `y = y + x`) that can be replaced with an operator assignment.\n\nThe quick-fix replaces the assignment with an assignment operator.\n\n**Example:**\n\n\n fun foo() {\n val list = mutableListOf(1, 2, 3)\n list = list + 4\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo() {\n val list = mutableListOf(1, 2, 3)\n list += 4\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceWithOperatorAssignment", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RemoveExplicitSuperQualifier", + "shortDescription": { + "text": "Unnecessary supertype qualification" + }, + "fullDescription": { + "text": "Reports 'super' member calls with redundant supertype qualification. Code in a derived class can call its superclass functions and property accessors implementations using the 'super' keyword. To specify the supertype from which the inherited implementation is taken, 'super' can be qualified by the supertype name in angle brackets, e.g. 'super'. Sometimes this qualification is redundant and can be omitted. Use the 'Remove explicit supertype qualification' quick-fix to clean up the code. Examples: 'open class B {\n open fun foo(){}\n }\n\n class A : B() {\n override fun foo() {\n super.foo() // <== redundant because 'B' is the only supertype\n }\n }\n\n interface I {\n fun foo() {}\n }\n\n class C : B(), I {\n override fun foo() {\n super.foo() // <== here qualifier is needed to distinguish 'B.foo()' from 'I.foo()'\n }\n }' After the quick-fix is applied: 'open class B {\n open fun foo(){}\n }\n\n class A : B() {\n override fun foo() {\n super.foo() // <== Updated\n }\n }\n\n interface I {\n fun foo() {}\n }\n\n class C : B(), I {\n override fun foo() {\n super.foo()\n }\n }'", + "markdown": "Reports `super` member calls with redundant supertype qualification.\n\n\nCode in a derived class can call its superclass functions and property accessors implementations using the `super` keyword.\nTo specify the supertype from which the inherited implementation is taken, `super` can be qualified by the supertype name in\nangle brackets, e.g. `super`. Sometimes this qualification is redundant and can be omitted.\nUse the 'Remove explicit supertype qualification' quick-fix to clean up the code.\n\n**Examples:**\n\n\n open class B {\n open fun foo(){}\n }\n\n class A : B() {\n override fun foo() {\n super.foo() // <== redundant because 'B' is the only supertype\n }\n }\n\n interface I {\n fun foo() {}\n }\n\n class C : B(), I {\n override fun foo() {\n super.foo() // <== here qualifier is needed to distinguish 'B.foo()' from 'I.foo()'\n }\n }\n\nAfter the quick-fix is applied:\n\n\n open class B {\n open fun foo(){}\n }\n\n class A : B() {\n override fun foo() {\n super.foo() // <== Updated\n }\n }\n\n interface I {\n fun foo() {}\n }\n\n class C : B(), I {\n override fun foo() {\n super.foo()\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RemoveExplicitSuperQualifier", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantExplicitType", + "shortDescription": { + "text": "Obvious explicit type" + }, + "fullDescription": { + "text": "Reports local variables' explicitly given types which are obvious and thus redundant, like 'val f: Foo = Foo()'. Example: 'class Point(val x: Int, val y: Int)\n\n fun foo() {\n val t: Boolean = true\n val p: Point = Point(1, 2)\n val i: Int = 42\n }' After the quick-fix is applied: 'class Point(val x: Int, val y: Int)\n\n fun foo() {\n val t = true\n val p = Point(1, 2)\n val i = 42\n }'", + "markdown": "Reports local variables' explicitly given types which are obvious and thus redundant, like `val f: Foo = Foo()`.\n\n**Example:**\n\n\n class Point(val x: Int, val y: Int)\n\n fun foo() {\n val t: Boolean = true\n val p: Point = Point(1, 2)\n val i: Int = 42\n }\n\nAfter the quick-fix is applied:\n\n\n class Point(val x: Int, val y: Int)\n\n fun foo() {\n val t = true\n val p = Point(1, 2)\n val i = 42\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantExplicitType", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnusedSymbol", + "shortDescription": { + "text": "Unused symbol" + }, + "fullDescription": { + "text": "Reports symbols that are not used or not reachable from entry points.", + "markdown": "Reports symbols that are not used or not reachable from entry points." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "unused", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspiciousVarProperty", + "shortDescription": { + "text": "Suspicious 'var' property: its setter does not influence its getter result" + }, + "fullDescription": { + "text": "Reports 'var' properties with default setter and getter that do not reference backing field. Such properties do not affect calling its setter; therefore, it will be clearer to change such property to 'val' and delete the initializer. Change to val and delete initializer quick-fix can be used to amend the code automatically. Example: '// This property always returns '1' and it doesn't important that the property is a 'var'\n var foo: Int = 0\n get() = 1'", + "markdown": "Reports `var` properties with default setter and getter that do not reference backing field.\n\n\nSuch properties do not affect calling its setter; therefore, it will be clearer to change such property to `val` and delete the initializer.\n\n**Change to val and delete initializer** quick-fix can be used to amend the code automatically.\n\nExample:\n\n\n // This property always returns '1' and it doesn't important that the property is a 'var'\n var foo: Int = 0\n get() = 1\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SuspiciousVarProperty", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceCollectionCountWithSize", + "shortDescription": { + "text": "Collection count can be converted to size" + }, + "fullDescription": { + "text": "Reports calls to 'Collection.count()'. This function call can be replaced with '.size'. '.size' form ensures that the operation is O(1) and won't allocate extra objects, whereas 'count()' could be confused with 'Iterable.count()', which is O(n) and allocating. Example: 'fun foo() {\n var list = listOf(1,2,3)\n list.count() // replaceable 'count()'\n }' After the quick-fix is applied: 'fun foo() {\n var list = listOf(1,2,3)\n list.size\n }'", + "markdown": "Reports calls to `Collection.count()`.\n\n\nThis function call can be replaced with `.size`.\n\n\n`.size` form ensures that the operation is O(1) and won't allocate extra objects, whereas\n`count()` could be confused with `Iterable.count()`, which is O(n) and allocating.\n\n\n**Example:**\n\n fun foo() {\n var list = listOf(1,2,3)\n list.count() // replaceable 'count()'\n }\n\nAfter the quick-fix is applied:\n\n fun foo() {\n var list = listOf(1,2,3)\n list.size\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceCollectionCountWithSize", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceArrayEqualityOpWithArraysEquals", + "shortDescription": { + "text": "Arrays comparison via '==' and '!='" + }, + "fullDescription": { + "text": "Reports usages of '==' or '!=' operator for arrays that should be replaced with 'contentEquals()'. The '==' and '!='operators compare array references instead of their content. Examples: 'fun test() {\n val a = arrayOf(1, 2, 3)\n val b = arrayOf(1, 2, 3)\n println(a == b) // references comparison\n }' After the quick-fix is applied: 'fun test() {\n val a = arrayOf(1, 2, 3)\n val b = arrayOf(1, 2, 3)\n println(a.contentEquals(b))\n }'", + "markdown": "Reports usages of `==` or `!=` operator for arrays that should be replaced with `contentEquals()`.\n\n\nThe `==` and `!=`operators compare array references instead of their content.\n\n**Examples:**\n\n fun test() {\n val a = arrayOf(1, 2, 3)\n val b = arrayOf(1, 2, 3)\n println(a == b) // references comparison\n }\n\nAfter the quick-fix is applied:\n\n fun test() {\n val a = arrayOf(1, 2, 3)\n val b = arrayOf(1, 2, 3)\n println(a.contentEquals(b))\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ReplaceArrayEqualityOpWithArraysEquals", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JavaCollectionsStaticMethod", + "shortDescription": { + "text": "Java Collections static method call can be replaced with Kotlin stdlib" + }, + "fullDescription": { + "text": "Reports a Java 'Collections' static method call that can be replaced with Kotlin stdlib. Example: 'import java.util.Collections\n\n fun test() {\n val mutableList = mutableListOf(1, 2)\n Collections.fill(mutableList, 3)\n }' The quick fix replaces Java 'Collections' static method call with the corresponding Kotlin stdlib method call: 'import java.util.Collections\n\n fun test() {\n val mutableList = mutableListOf(1, 2)\n mutableList.fill(3)\n }'", + "markdown": "Reports a Java `Collections` static method call that can be replaced with Kotlin stdlib.\n\n**Example:**\n\n\n import java.util.Collections\n\n fun test() {\n val mutableList = mutableListOf(1, 2)\n Collections.fill(mutableList, 3)\n }\n\nThe quick fix replaces Java `Collections` static method call with the corresponding Kotlin stdlib method call:\n\n\n import java.util.Collections\n\n fun test() {\n val mutableList = mutableListOf(1, 2)\n mutableList.fill(3)\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "JavaCollectionsStaticMethod", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MoveVariableDeclarationIntoWhen", + "shortDescription": { + "text": "Variable declaration could be moved inside 'when'" + }, + "fullDescription": { + "text": "Reports variable declarations that can be moved inside a 'when' expression. Example: 'fun someCalc(x: Int) = x * 42\n\nfun foo(x: Int): Int {\n val a = someCalc(x)\n return when (a) {\n 1 -> a\n 2 -> 2 * a\n else -> 24\n }\n}' After the quick-fix is applied: 'fun foo(x: Int): Int {\n return when (val a = someCalc(x)) {\n 1 -> a\n 2 -> 2 * a\n else -> 24\n }\n}'", + "markdown": "Reports variable declarations that can be moved inside a `when` expression.\n\n**Example:**\n\n\n fun someCalc(x: Int) = x * 42\n\n fun foo(x: Int): Int {\n val a = someCalc(x)\n return when (a) {\n 1 -> a\n 2 -> 2 * a\n else -> 24\n }\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(x: Int): Int {\n return when (val a = someCalc(x)) {\n 1 -> a\n 2 -> 2 * a\n else -> 24\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "MoveVariableDeclarationIntoWhen", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DeprecatedGradleDependency", + "shortDescription": { + "text": "Deprecated library is used in Gradle" + }, + "fullDescription": { + "text": "Reports deprecated dependencies in Gradle build scripts. Example: 'dependencies {\n compile \"org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.0\"\n }' After the quick-fix applied: 'dependencies {\n compile \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.0\"\n }'", + "markdown": "Reports deprecated dependencies in Gradle build scripts.\n\n**Example:**\n\n\n dependencies {\n compile \"org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.0\"\n }\n\nAfter the quick-fix applied:\n\n\n dependencies {\n compile \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.0\"\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "DeprecatedGradleDependency", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin", + "index": 3, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CastDueToProgressionResolutionChangeMigration", + "shortDescription": { + "text": "Progression resolution change since 1.9" + }, + "fullDescription": { + "text": "Reports overloaded function calls where an argument requires an explicit cast to resolve to a proper declaration. The current compiler warning (available since Kotlin 1.6.20) will become an error in Kotlin 1.8. Progressions and ranges types ('kotlin.ranges') will start implementing the 'Collection' interface in Kotlin 1.9 and later. This update will cause a change in resolution for overloaded functions. For instance, in the example below, the 'test(1..5)' call will be resolved to 'test(t: Any)' in Kotlin 1.8 and earlier and to 'test(t: Collection<*>)' in Kotlin 1.9 and later. 'fun test(t: Any) { }\n fun test(t: Collection<*>) { }\n fun invoke() {\n test(1..5) // IntRange becomes Collection in 1.9\n }' The provided quick-fix captures the behaviour specific to the compiler of version 1.8 and earlier: 'fun test(t: Any) { }\n fun test(t: Collection<*>) { }\n\n fun invoke() {\n test(1..5) // resolved to 'test(t: T)' before Kotlin 1.9\n }' After the quick-fix is applied: 'fun test(t: Any) { }\n fun test(t: Collection<*>) { }\n\n fun invoke() {\n test((1..5) as Iterable) // resolved to 'test(t: T)' in Kotlin 1.9\n }' Inspection is available for the Kotlin language level starting from 1.6.", + "markdown": "Reports overloaded function calls where an argument requires an explicit cast to resolve to a proper declaration.\nThe current compiler warning (available since Kotlin 1.6.20) will become an error in Kotlin 1.8.\n\n\nProgressions and ranges types (`kotlin.ranges`) will start implementing the `Collection` interface in Kotlin\n1.9 and later. This update will cause a change in resolution for overloaded functions. For instance, in the example below, the\n`test(1..5)` call will be resolved to `test(t: Any)` in Kotlin 1.8 and earlier and to\n`test(t: Collection<*>)` in Kotlin 1.9 and later.\n\n\n fun test(t: Any) { }\n fun test(t: Collection<*>) { }\n fun invoke() {\n test(1..5) // IntRange becomes Collection in 1.9\n }\n\nThe provided quick-fix captures the behaviour specific to the compiler of version 1.8 and earlier:\n\n\n fun test(t: Any) { }\n fun test(t: Collection<*>) { }\n\n fun invoke() {\n test(1..5) // resolved to 'test(t: T)' before Kotlin 1.9\n }\n\nAfter the quick-fix is applied:\n\n\n fun test(t: Any) { }\n fun test(t: Collection<*>) { }\n\n fun invoke() {\n test((1..5) as Iterable) // resolved to 'test(t: T)' in Kotlin 1.9\n }\n\nInspection is available for the Kotlin language level starting from 1.6." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CastDueToProgressionResolutionChangeMigration", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Migration", + "index": 16, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RemoveEmptyParenthesesFromAnnotationEntry", + "shortDescription": { + "text": "Remove unnecessary parentheses" + }, + "fullDescription": { + "text": "Reports redundant empty parentheses in annotation entries. Use the 'Remove unnecessary parentheses' quick-fix to clean up the code. Examples: 'annotation class MyAnnotationA\n annotation class MyAnnotationB(val x: Int)\n annotation class MyAnnotationC(val x: Int = 10) // default value is present\n\n @MyAnnotationA() // <== parentheses are redundant\n fun testA() {\n }\n\n @MyAnnotationB() // <== missing argument, parentheses are required\n fun testB() {\n }\n\n @MyAnnotationC() // <== parentheses are redundant\n fun testC() {\n }'", + "markdown": "Reports redundant empty parentheses in annotation entries.\n\nUse the 'Remove unnecessary parentheses' quick-fix to clean up the code.\n\n**Examples:**\n\n\n annotation class MyAnnotationA\n annotation class MyAnnotationB(val x: Int)\n annotation class MyAnnotationC(val x: Int = 10) // default value is present\n\n @MyAnnotationA() // <== parentheses are redundant\n fun testA() {\n }\n\n @MyAnnotationB() // <== missing argument, parentheses are required\n fun testB() {\n }\n\n @MyAnnotationC() // <== parentheses are redundant\n fun testC() {\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RemoveEmptyParenthesesFromAnnotationEntry", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SimplifiableCallChain", + "shortDescription": { + "text": "Call chain on collection type can be simplified" + }, + "fullDescription": { + "text": "Reports two-call chains replaceable by a single call. It can help you to avoid redundant code execution. The quick-fix replaces the call chain with a single call. Example: 'fun main() {\n listOf(1, 2, 3).filter { it > 1 }.count()\n }' After the quick-fix is applied: 'fun main() {\n listOf(1, 2, 3).count { it > 1 }\n }'", + "markdown": "Reports two-call chains replaceable by a single call.\n\nIt can help you to avoid redundant code execution.\n\nThe quick-fix replaces the call chain with a single call.\n\n**Example:**\n\n\n fun main() {\n listOf(1, 2, 3).filter { it > 1 }.count()\n }\n\nAfter the quick-fix is applied:\n\n\n fun main() {\n listOf(1, 2, 3).count { it > 1 }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "SimplifiableCallChain", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConvertReferenceToLambda", + "shortDescription": { + "text": "Can be replaced with lambda" + }, + "fullDescription": { + "text": "Reports a function reference expression that can be replaced with a function literal (lambda). Sometimes, passing a lambda looks more straightforward and more consistent with the rest of the code. Also, the fix might be handy if you need to replace a simple call with something more complex. Example: 'fun Int.isEven() = this % 2 == 0\n\n fun example() {\n val numbers = listOf(1, 2, 4, 7, 9, 10)\n val evenNumbers = numbers.filter(Int::isEven)\n }' After the quick-fix is applied: 'fun Int.isEven() = this % 2 == 0\n\n fun example() {\n val numbers = listOf(1, 2, 4, 7, 9, 10)\n val evenNumbers = numbers.filter { it.isEven() }\n }'", + "markdown": "Reports a function reference expression that can be replaced with a function literal (lambda).\n\n\nSometimes, passing a lambda looks more straightforward and more consistent with the rest of the code.\nAlso, the fix might be handy if you need to replace a simple call with something more complex.\n\n**Example:**\n\n\n fun Int.isEven() = this % 2 == 0\n\n fun example() {\n val numbers = listOf(1, 2, 4, 7, 9, 10)\n val evenNumbers = numbers.filter(Int::isEven)\n }\n\nAfter the quick-fix is applied:\n\n\n fun Int.isEven() = this % 2 == 0\n\n fun example() {\n val numbers = listOf(1, 2, 4, 7, 9, 10)\n val evenNumbers = numbers.filter { it.isEven() }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ConvertReferenceToLambda", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceCallWithBinaryOperator", + "shortDescription": { + "text": "Can be replaced with binary operator" + }, + "fullDescription": { + "text": "Reports function calls that can be replaced with binary operators, in particular comparison-related ones. Example: 'fun test(): Boolean {\n return 2.compareTo(1) > 0 // replaceable 'compareTo()'\n }' After the quick-fix is applied: 'fun test(): Boolean {\n return 2 > 1\n }'", + "markdown": "Reports function calls that can be replaced with binary operators, in particular comparison-related ones.\n\n**Example:**\n\n fun test(): Boolean {\n return 2.compareTo(1) > 0 // replaceable 'compareTo()'\n }\n\nAfter the quick-fix is applied:\n\n fun test(): Boolean {\n return 2 > 1\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceCallWithBinaryOperator", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnlabeledReturnInsideLambda", + "shortDescription": { + "text": "Unlabeled return inside lambda" + }, + "fullDescription": { + "text": "Reports unlabeled 'return' expressions inside inline lambda. Such expressions can be confusing because it might be unclear which scope belongs to 'return'. Change to return@… quick-fix can be used to amend the code automatically. Example: 'fun test(list: List) {\n list.forEach {\n // This return expression returns from the function test\n // One can change it to return@forEach to change the scope\n if (it == 10) return\n }\n }' After the quick-fix is applied: 'fun test(list: List) {\n list.forEach {\n if (it == 10) return@test\n }\n }'", + "markdown": "Reports unlabeled `return` expressions inside inline lambda.\n\nSuch expressions can be confusing because it might be unclear which scope belongs to `return`.\n\n**Change to return@...** quick-fix can be used to amend the code automatically.\n\nExample:\n\n\n fun test(list: List) {\n list.forEach {\n // This return expression returns from the function test\n // One can change it to return@forEach to change the scope\n if (it == 10) return\n }\n }\n\nAfter the quick-fix is applied:\n\n\n fun test(list: List) {\n list.forEach {\n if (it == 10) return@test\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "UnlabeledReturnInsideLambda", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnusedUnaryOperator", + "shortDescription": { + "text": "Unused unary operator" + }, + "fullDescription": { + "text": "Reports unary operators for number types on unused expressions. Unary operators break previous expression if they are used without braces. As a result, mathematical expressions spanning multi lines can be misleading. Example: 'fun main() {\n val result = 1 + 2 * 3\n + 3 // <== note that '+ 3' doesn't belong to the 'result' variable, it is unused\n println(\"Result = $result\") // The result is '7' and not '10' as it might be expected\n }'", + "markdown": "Reports unary operators for number types on unused expressions.\n\nUnary operators break previous expression if they are used without braces.\nAs a result, mathematical expressions spanning multi lines can be misleading.\n\nExample:\n\n\n fun main() {\n val result = 1 + 2 * 3\n + 3 // <== note that '+ 3' doesn't belong to the 'result' variable, it is unused\n println(\"Result = $result\") // The result is '7' and not '10' as it might be expected\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "UnusedUnaryOperator", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AddConversionCallMigration", + "shortDescription": { + "text": "Explicit conversion from `Int` needed since 1.9" + }, + "fullDescription": { + "text": "Reports expressions that will be of type 'Int', thus causing compilation errors in Kotlin 1.9 and later. Example: 'fun takeByte(x: Byte) {}\n\n fun foo() {\n takeByte(1 + 1) // will be resolved to Int in 1.9\n }' After the quick-fix is applied: 'fun takeByte(x: Byte) {}\n\n fun foo() {\n takeByte((1 + 1).toByte()) // will be resolved to Int in 1.9\n }' Inspection is available for Kotlin language level starting from 1.7.", + "markdown": "Reports expressions that will be of type `Int`, thus causing compilation errors in Kotlin 1.9 and later.\n\nExample:\n\n\n fun takeByte(x: Byte) {}\n\n fun foo() {\n takeByte(1 + 1) // will be resolved to Int in 1.9\n }\n\nAfter the quick-fix is applied:\n\n\n fun takeByte(x: Byte) {}\n\n fun foo() {\n takeByte((1 + 1).toByte()) // will be resolved to Int in 1.9\n }\n\nInspection is available for Kotlin language level starting from 1.7." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "AddConversionCallMigration", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Migration", + "index": 16, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LateinitVarOverridesLateinitVar", + "shortDescription": { + "text": "'lateinit var' property overrides 'lateinit var' property" + }, + "fullDescription": { + "text": "Reports 'lateinit var' properties that override other 'lateinit var' properties. A subclass instance will have two fields for a single property, and the one from the superclass will remain effectively unused. Example: 'open class BaseClass {\n open lateinit var name: String\n }\n\n class RealClass : BaseClass() {\n override lateinit var name: String\n }'", + "markdown": "Reports `lateinit var` properties that override other `lateinit var` properties.\n\nA subclass instance will have two fields for a single property, and the one from the superclass will remain effectively unused.\n\n**Example:**\n\n\n open class BaseClass {\n open lateinit var name: String\n }\n\n class RealClass : BaseClass() {\n override lateinit var name: String\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "LateinitVarOverridesLateinitVar", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "VerboseNullabilityAndEmptiness", + "shortDescription": { + "text": "Verbose nullability and emptiness check" + }, + "fullDescription": { + "text": "Reports combination of 'null' and emptiness checks that can be simplified into a single check. The quick-fix replaces highlighted checks with a combined check call, such as 'isNullOrEmpty()'. Example: 'fun test(list: List?) {\n if (list == null || list.isEmpty()) {\n println(\"List is empty!\")\n } else {\n println(list.joinToString())\n }\n }' After the quick-fix is applied: 'fun test(list: List?) {\n if (list.isNullOrEmpty()) {\n println(\"List is empty!\")\n } else {\n println(list.joinToString())\n }\n }'", + "markdown": "Reports combination of `null` and emptiness checks that can be simplified into a single check.\n\nThe quick-fix replaces highlighted checks with a combined check call, such as `isNullOrEmpty()`.\n\n**Example:**\n\n\n fun test(list: List?) {\n if (list == null || list.isEmpty()) {\n println(\"List is empty!\")\n } else {\n println(list.joinToString())\n }\n }\n\nAfter the quick-fix is applied:\n\n\n fun test(list: List?) {\n if (list.isNullOrEmpty()) {\n println(\"List is empty!\")\n } else {\n println(list.joinToString())\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "VerboseNullabilityAndEmptiness", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClassName", + "shortDescription": { + "text": "Class naming convention" + }, + "fullDescription": { + "text": "Reports class names that do not follow the recommended naming conventions. Consistent naming allows for easier code reading and understanding. According to the Kotlin official style guide, class names should start with an uppercase letter and use camel case. It is possible to introduce other naming rules by changing the \"Pattern\" regular expression. Example: 'class user(val name: String)' The quick-fix renames the class according to the Kotlin naming conventions: 'class User(val name: String)'", + "markdown": "Reports class names that do not follow the recommended naming conventions.\n\n\nConsistent naming allows for easier code reading and understanding.\nAccording to the [Kotlin official style guide](https://kotlinlang.org/docs/coding-conventions.html#naming-rules),\nclass names should start with an uppercase letter and use camel case.\n\nIt is possible to introduce other naming rules by changing the \"Pattern\" regular expression.\n\n**Example:**\n\n\n class user(val name: String)\n\nThe quick-fix renames the class according to the Kotlin naming conventions:\n\n\n class User(val name: String)\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ClassName", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Naming conventions", + "index": 49, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RemoveEmptyPrimaryConstructor", + "shortDescription": { + "text": "Redundant empty primary constructor" + }, + "fullDescription": { + "text": "Reports empty primary constructors when they are implicitly available anyway. A primary constructor is redundant and can be safely omitted when it does not have any annotations or visibility modifiers. Use the 'Remove empty primary constructor' quick-fix to clean up the code. Examples: 'class MyClassA constructor() // redundant, can be replaced with 'class MyClassA'\n\n annotation class MyAnnotation\n class MyClassB @MyAnnotation constructor() // required because of annotation\n\n class MyClassC private constructor() // required because of visibility modifier'", + "markdown": "Reports empty primary constructors when they are implicitly available anyway.\n\n\nA primary constructor is redundant and can be safely omitted when it does not have any annotations or visibility modifiers.\nUse the 'Remove empty primary constructor' quick-fix to clean up the code.\n\n**Examples:**\n\n\n class MyClassA constructor() // redundant, can be replaced with 'class MyClassA'\n\n annotation class MyAnnotation\n class MyClassB @MyAnnotation constructor() // required because of annotation\n\n class MyClassC private constructor() // required because of visibility modifier\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RemoveEmptyPrimaryConstructor", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RemoveEmptySecondaryConstructorBody", + "shortDescription": { + "text": "Redundant constructor body" + }, + "fullDescription": { + "text": "Reports empty bodies of secondary constructors.", + "markdown": "Reports empty bodies of secondary constructors." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RemoveEmptySecondaryConstructorBody", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FloatingPointLiteralPrecision", + "shortDescription": { + "text": "Floating-point literal exceeds the available precision" + }, + "fullDescription": { + "text": "Reports floating-point literals that cannot be represented with the required precision using IEEE 754 'Float' and 'Double' types. For example, '1.9999999999999999999' has too many significant digits, so its representation as a 'Double' will be rounded to '2.0'. Specifying excess digits may be misleading as it hides the fact that computations use rounded values instead. The quick-fix replaces the literal with a rounded value that matches the actual representation of the constant. Example: 'val x: Float = 3.14159265359f' After the quick-fix is applied: 'val x: Float = 3.1415927f'", + "markdown": "Reports floating-point literals that cannot be represented with the required precision using [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) `Float` and `Double` types.\n\n\nFor example, `1.9999999999999999999` has too many significant digits,\nso its representation as a `Double` will be rounded to `2.0`.\nSpecifying excess digits may be misleading as it hides the fact that computations\nuse rounded values instead.\n\n\nThe quick-fix replaces the literal with a rounded value that matches the actual representation\nof the constant.\n\n**Example:**\n\n\n val x: Float = 3.14159265359f\n\nAfter the quick-fix is applied:\n\n\n val x: Float = 3.1415927f\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "note", + "parameters": { + "suppressToolId": "FloatingPointLiteralPrecision", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Other problems", + "index": 54, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DifferentKotlinGradleVersion", + "shortDescription": { + "text": "Kotlin Gradle and IDE plugins versions are different" + }, + "fullDescription": { + "text": "Reports that Gradle plugin version isn't properly supported in the current IDE plugin. This can cause inconsistencies between IDE and Gradle builds in error reporting or code behavior. Example: 'dependencies {\n classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:0.0.1\"\n }' To fix the problem change the kotlin gradle plugin version to match the version of kotlin that is bundled into the IDE plugin.", + "markdown": "Reports that Gradle plugin version isn't properly supported in the current IDE plugin.\n\nThis can cause inconsistencies between IDE and Gradle builds in error reporting or code behavior.\n\n**Example:**\n\n\n dependencies {\n classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:0.0.1\"\n }\n\nTo fix the problem change the kotlin gradle plugin version to match the version of kotlin that is bundled into the IDE plugin." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "DifferentKotlinGradleVersion", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin", + "index": 3, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConvertPairConstructorToToFunction", + "shortDescription": { + "text": "Convert Pair constructor to 'to' function" + }, + "fullDescription": { + "text": "Reports a 'Pair' constructor invocation that can be replaced with a 'to()' infix function call. Explicit constructor invocations may add verbosity, especially if they are used multiple times. Replacing constructor calls with 'to()' makes code easier to read and maintain. Example: 'val countries = mapOf(\n Pair(\"France\", \"Paris\"),\n Pair(\"Spain\", \"Madrid\"),\n Pair(\"Germany\", \"Berlin\")\n )' After the quick-fix is applied: 'val countries = mapOf(\n \"France\" to \"Paris\",\n \"Spain\" to \"Madrid\",\n \"Germany\" to \"Berlin\"\n )'", + "markdown": "Reports a `Pair` constructor invocation that can be replaced with a `to()` infix function call.\n\n\nExplicit constructor invocations may add verbosity, especially if they are used multiple times.\nReplacing constructor calls with `to()` makes code easier to read and maintain.\n\n**Example:**\n\n\n val countries = mapOf(\n Pair(\"France\", \"Paris\"),\n Pair(\"Spain\", \"Madrid\"),\n Pair(\"Germany\", \"Berlin\")\n )\n\nAfter the quick-fix is applied:\n\n\n val countries = mapOf(\n \"France\" to \"Paris\",\n \"Spain\" to \"Madrid\",\n \"Germany\" to \"Berlin\"\n )\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ConvertPairConstructorToToFunction", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantGetter", + "shortDescription": { + "text": "Redundant property getter" + }, + "fullDescription": { + "text": "Reports redundant property getters. Example: 'class Test {\n val a = 1\n get\n val b = 1\n get() = field\n }' After the quick-fix is applied: 'class Test {\n val a = 1\n val b = 1\n }'", + "markdown": "Reports redundant property getters.\n\n**Example:**\n\n\n class Test {\n val a = 1\n get\n val b = 1\n get() = field\n }\n\nAfter the quick-fix is applied:\n\n\n class Test {\n val a = 1\n val b = 1\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RedundantGetter", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantIf", + "shortDescription": { + "text": "Redundant 'if' statement" + }, + "fullDescription": { + "text": "Reports 'if' statements which can be simplified to a single statement. Example: 'fun test(): Boolean {\n if (foo()) {\n return true\n } else {\n return false\n }\n }' After the quick-fix is applied: 'fun test(): Boolean {\n return foo()\n }'", + "markdown": "Reports `if` statements which can be simplified to a single statement.\n\n**Example:**\n\n\n fun test(): Boolean {\n if (foo()) {\n return true\n } else {\n return false\n }\n }\n\nAfter the quick-fix is applied:\n\n\n fun test(): Boolean {\n return foo()\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RedundantIf", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KDocMissingDocumentation", + "shortDescription": { + "text": "Missing KDoc comments for public declarations" + }, + "fullDescription": { + "text": "Reports public declarations that do not have KDoc comments. Example: 'class A' The quick fix generates the comment block above the declaration: '/**\n *\n */\n class A'", + "markdown": "Reports public declarations that do not have KDoc comments.\n\n**Example:**\n\n\n class A\n\nThe quick fix generates the comment block above the declaration:\n\n\n /**\n *\n */\n class A\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "KDocMissingDocumentation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Other problems", + "index": 54, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KotlinEqualsBetweenInconvertibleTypes", + "shortDescription": { + "text": "'equals()' between objects of inconvertible types" + }, + "fullDescription": { + "text": "Reports calls to 'equals()' where the receiver and the argument are of incompatible primitive, enum, or string types. While such a call might theoretically be useful, most likely it represents a bug. Example: '5.equals(\"\");'", + "markdown": "Reports calls to `equals()` where the receiver and the argument are of incompatible primitive, enum, or string types.\n\nWhile such a call might theoretically be useful, most likely it represents a bug.\n\n**Example:**\n\n 5.equals(\"\");\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "EqualsBetweenInconvertibleTypes", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JoinDeclarationAndAssignment", + "shortDescription": { + "text": "Join declaration and assignment" + }, + "fullDescription": { + "text": "Reports property declarations that can be joined with the following assignment. Example: 'val x: String\n x = System.getProperty(\"\")' The quick fix joins the declaration with the assignment: 'val x = System.getProperty(\"\")' Configure the inspection: You can disable the option Report with complex initialization of member properties to skip properties with complex initialization. This covers two cases: The property initializer is complex (it is a multiline or a compound/control-flow expression) The property is first initialized and then immediately used in subsequent code (for example, to call additional initialization methods)", + "markdown": "Reports property declarations that can be joined with the following assignment.\n\n**Example:**\n\n\n val x: String\n x = System.getProperty(\"\")\n\nThe quick fix joins the declaration with the assignment:\n\n\n val x = System.getProperty(\"\")\n\nConfigure the inspection:\n\nYou can disable the option **Report with complex initialization of member properties** to skip properties with complex initialization. This covers two cases:\n\n1. The property initializer is complex (it is a multiline or a compound/control-flow expression)\n2. The property is first initialized and then immediately used in subsequent code (for example, to call additional initialization methods)" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "JoinDeclarationAndAssignment", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RemoveExplicitTypeArguments", + "shortDescription": { + "text": "Unnecessary type argument" + }, + "fullDescription": { + "text": "Reports function calls with type arguments that can be automatically inferred. Such type arguments are redundant and can be safely omitted. Use the 'Remove explicit type arguments' quick-fix to clean up the code. Examples: '// 'String' type can be inferred here\n fun foo(): MutableList = mutableListOf()\n\n // Here 'String' cannot be inferred, type argument is required.\n fun bar() = mutableListOf()' After the quick-fix is applied: 'fun foo(): MutableList = mutableListOf() <== Updated\n\n fun bar() = mutableListOf()'", + "markdown": "Reports function calls with type arguments that can be automatically inferred. Such type arguments are redundant and can be safely omitted.\n\nUse the 'Remove explicit type arguments' quick-fix to clean up the code.\n\n**Examples:**\n\n\n // 'String' type can be inferred here\n fun foo(): MutableList = mutableListOf()\n\n // Here 'String' cannot be inferred, type argument is required.\n fun bar() = mutableListOf()\n\nAfter the quick-fix is applied:\n\n\n fun foo(): MutableList = mutableListOf() <== Updated\n\n fun bar() = mutableListOf()\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RemoveExplicitTypeArguments", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "HasPlatformType", + "shortDescription": { + "text": "Function or property has platform type" + }, + "fullDescription": { + "text": "Reports functions and properties that have a platform type. To prevent unexpected errors, the type should be declared explicitly. Example: 'fun foo() = java.lang.String.valueOf(1)' The quick fix allows you to specify the return type: 'fun foo(): String = java.lang.String.valueOf(1)'", + "markdown": "Reports functions and properties that have a platform type.\n\nTo prevent unexpected errors, the type should be declared explicitly.\n\n**Example:**\n\n\n fun foo() = java.lang.String.valueOf(1)\n\nThe quick fix allows you to specify the return type:\n\n\n fun foo(): String = java.lang.String.valueOf(1)\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "note", + "parameters": { + "suppressToolId": "HasPlatformType", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Java interop issues", + "index": 70, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantVisibilityModifier", + "shortDescription": { + "text": "Redundant visibility modifier" + }, + "fullDescription": { + "text": "Reports visibility modifiers that match the default visibility of an element ('public' for most elements, 'protected' for members that override a protected member).", + "markdown": "Reports visibility modifiers that match the default visibility of an element (`public` for most elements, `protected` for members that override a protected member)." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantVisibilityModifier", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EnumValuesSoftDeprecateInJava", + "shortDescription": { + "text": "'Enum.values()' is recommended to be replaced by 'Enum.getEntries()' since Kotlin 1.9" + }, + "fullDescription": { + "text": "Reports calls from Java to 'values()' method of Kotlin enum classes that can be replaced with 'getEntries()'. Use of 'Enum.getEntries()' may improve performance of your code. More details: KT-48872 Provide modern and performant replacement for Enum.values()", + "markdown": "Reports calls from Java to `values()` method of Kotlin enum classes that can be replaced with `getEntries()`.\n\n\nUse of `Enum.getEntries()` may improve performance of your code.\n\n\n**More details:** [KT-48872 Provide modern and performant replacement for Enum.values()](https://youtrack.jetbrains.com/issue/KT-48872)" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "EnumValuesSoftDeprecateInJava", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Other problems", + "index": 54, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UsePropertyAccessSyntax", + "shortDescription": { + "text": "Accessor call that can be replaced with property access syntax" + }, + "fullDescription": { + "text": "Reports Java 'get' and 'set' method calls that can be replaced with the Kotlin synthetic properties. Use property access syntax quick-fix can be used to amend the code automatically. Example: '// Java:\n public class JavaClassWithGetter {\n private final String expr = \"result\";\n\n // ...\n\n public String getExpr() {\n return expr;\n }\n }' '// Kotlin:\n fun test(j: JavaClassWithGetter) {\n // ...\n j.getExpr() // <== The quick-fix simplifies the expression to 'j.expr'\n }'", + "markdown": "Reports Java `get` and `set` method calls that can be replaced with the Kotlin synthetic properties.\n\n**Use property access syntax** quick-fix can be used to amend the code automatically.\n\nExample:\n\n\n // Java:\n public class JavaClassWithGetter {\n private final String expr = \"result\";\n\n // ...\n\n public String getExpr() {\n return expr;\n }\n }\n\n\n // Kotlin:\n fun test(j: JavaClassWithGetter) {\n // ...\n j.getExpr() // <== The quick-fix simplifies the expression to 'j.expr'\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "UsePropertyAccessSyntax", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UseExpressionBody", + "shortDescription": { + "text": "Expression body syntax is preferable here" + }, + "fullDescription": { + "text": "Reports 'return' expressions (one-liners or 'when') that can be replaced with expression body syntax. Expression body syntax is recommended by the style guide. Convert to expression body quick-fix can be used to amend the code automatically. Example: 'fun sign(x: Int): Int {\n return when { // <== can be simplified\n x < 0 -> -1\n x > 0 -> 1\n else -> 0\n }\n }' After the quick-fix is applied: 'fun sign(x: Int): Int = when {\n x < 0 -> -1\n x > 0 -> 1\n else -> 0\n }'", + "markdown": "Reports `return` expressions (one-liners or `when`) that can be replaced with expression body syntax.\n\nExpression body syntax is recommended by the [style guide](https://kotlinlang.org/docs/coding-conventions.html#functions).\n\n**Convert to expression body** quick-fix can be used to amend the code automatically.\n\nExample:\n\n\n fun sign(x: Int): Int {\n return when { // <== can be simplified\n x < 0 -> -1\n x > 0 -> 1\n else -> 0\n }\n }\n\nAfter the quick-fix is applied:\n\n\n fun sign(x: Int): Int = when {\n x < 0 -> -1\n x > 0 -> 1\n else -> 0\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "UseExpressionBody", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DataClassPrivateConstructor", + "shortDescription": { + "text": "Private data class constructor is exposed via the 'copy' method" + }, + "fullDescription": { + "text": "Reports the 'private' primary constructor in data classes. 'data' classes have a 'copy()' factory method that can be used similarly to a constructor. A constructor should not be marked as 'private' to provide enough safety. Example: 'data class User private constructor(val name: String)' The quick-fix changes the constructor visibility modifier to 'public': 'data class User(val name: String)'", + "markdown": "Reports the `private` primary constructor in data classes.\n\n\n`data` classes have a `copy()` factory method that can be used similarly to a constructor.\nA constructor should not be marked as `private` to provide enough safety.\n\n**Example:**\n\n\n data class User private constructor(val name: String)\n\nThe quick-fix changes the constructor visibility modifier to `public`:\n\n\n data class User(val name: String)\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "DataClassPrivateConstructor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantInnerClassModifier", + "shortDescription": { + "text": "Redundant 'inner' modifier" + }, + "fullDescription": { + "text": "Reports the 'inner' modifier on a class as redundant if it doesn't reference members of its outer class. Example: 'class Foo {\n inner class InnerClass { // redundant `inner` modifier\n fun hello() {\n println(\"Hi!\")\n }\n }\n }\n\n class List {\n val objects = Array(42) { Any() }\n\n inner class Iterator { // Not redundant `inner` modifier\n fun next(): Any {\n return objects[0]\n }\n }\n }' After the quick-fix is applied: 'class Foo {\n class InnerClass { // redundant `inner` modifier\n fun hello() {\n println(\"Hi!\")\n }\n }\n }\n\n class List {\n val objects = Array(42) { Any() }\n\n inner class Iterator { // Not redundant `inner` modifier\n fun next(): Any {\n return objects[0]\n }\n }\n }'", + "markdown": "Reports the `inner` modifier on a class as redundant if it doesn't reference members of its outer class.\n\n**Example:**\n\n\n class Foo {\n inner class InnerClass { // redundant `inner` modifier\n fun hello() {\n println(\"Hi!\")\n }\n }\n }\n\n class List {\n val objects = Array(42) { Any() }\n\n inner class Iterator { // Not redundant `inner` modifier\n fun next(): Any {\n return objects[0]\n }\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo {\n class InnerClass { // redundant `inner` modifier\n fun hello() {\n println(\"Hi!\")\n }\n }\n }\n\n class List {\n val objects = Array(42) { Any() }\n\n inner class Iterator { // Not redundant `inner` modifier\n fun next(): Any {\n return objects[0]\n }\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantInnerClassModifier", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JavaCollectionsStaticMethodOnImmutableList", + "shortDescription": { + "text": "Call of Java mutator method on immutable Kotlin collection" + }, + "fullDescription": { + "text": "Reports Java mutator methods calls (like 'fill', 'reverse', 'shuffle', 'sort') on an immutable Kotlin collection. This can lead to 'UnsupportedOperationException' at runtime. Example: 'import java.util.Collections\n\n fun test() {\n val immutableList = listOf(1, 2)\n Collections.reverse(immutableList)\n }' To fix the problem make the list mutable.", + "markdown": "Reports Java mutator methods calls (like `fill`, `reverse`, `shuffle`, `sort`) on an immutable Kotlin collection.\n\nThis can lead to `UnsupportedOperationException` at runtime.\n\n**Example:**\n\n\n import java.util.Collections\n\n fun test() {\n val immutableList = listOf(1, 2)\n Collections.reverse(immutableList)\n }\n\nTo fix the problem make the list mutable." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "JavaCollectionsStaticMethodOnImmutableList", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Java interop issues", + "index": 70, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MapGetWithNotNullAssertionOperator", + "shortDescription": { + "text": "'map.get()' with not-null assertion operator (!!)" + }, + "fullDescription": { + "text": "Reports 'map.get()!!' that can be replaced with 'map.getValue()', 'map.getOrElse()', and so on. Example: 'fun test(map: Map): String = map.get(0)!!' After the quick-fix is applied: 'fun test(map: Map): String = map.getValue(0)'", + "markdown": "Reports `map.get()!!` that can be replaced with `map.getValue()`, `map.getOrElse()`, and so on.\n\n**Example:**\n\n\n fun test(map: Map): String = map.get(0)!!\n\nAfter the quick-fix is applied:\n\n\n fun test(map: Map): String = map.getValue(0)\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "MapGetWithNotNullAssertionOperator", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SortModifiers", + "shortDescription": { + "text": "Non-canonical modifier order" + }, + "fullDescription": { + "text": "Reports modifiers that do not follow the order recommended by the style guide. Sort modifiers quick-fix can be used to amend the code automatically. Examples: 'private inline fun correctOrder(f: () -> Unit) {} // <== Ok\n\n infix private fun Int.wrongOrder(expr: Int) {} // <== wrong order, quick-fix amends the modifiers to \"private infix\"'", + "markdown": "Reports modifiers that do not follow the order recommended by the [style guide](https://kotlinlang.org/docs/coding-conventions.html#modifiers-order).\n\n**Sort modifiers** quick-fix can be used to amend the code automatically.\n\nExamples:\n\n\n private inline fun correctOrder(f: () -> Unit) {} // <== Ok\n\n infix private fun Int.wrongOrder(expr: Int) {} // <== wrong order, quick-fix amends the modifiers to \"private infix\"\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "SortModifiers", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MavenCoroutinesDeprecation", + "shortDescription": { + "text": "Incompatible kotlinx.coroutines dependency is used with Kotlin 1.3+ in Maven" + }, + "fullDescription": { + "text": "Reports kotlinx.coroutines library dependencies in Maven that should be updated in order to be compatible with Kotlin 1.3 and later.", + "markdown": "Reports **kotlinx.coroutines** library dependencies in Maven that should be updated in order to be compatible with Kotlin 1.3 and later." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "MavenCoroutinesDeprecation", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Migration/Maven", + "index": 108, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NullableBooleanElvis", + "shortDescription": { + "text": "Equality check can be used instead of elvis for nullable boolean check" + }, + "fullDescription": { + "text": "Reports cases when an equality check should be used instead of the elvis operator. Example: 'fun check(a: Boolean? == null) {\n if (a ?: false) throw IllegalStateException()\n}' After the quick-fix is applied: 'fun check(a: Boolean? == null) {\n if (a == true) throw IllegalStateException()\n}'", + "markdown": "Reports cases when an equality check should be used instead of the elvis operator.\n\n**Example:**\n\n\n fun check(a: Boolean? == null) {\n if (a ?: false) throw IllegalStateException()\n }\n\nAfter the quick-fix is applied:\n\n\n fun check(a: Boolean? == null) {\n if (a == true) throw IllegalStateException()\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "NullableBooleanElvis", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EnumEntryName", + "shortDescription": { + "text": "Enum entry naming convention" + }, + "fullDescription": { + "text": "Reports enum entry names that do not follow the recommended naming conventions. Example: 'enum class Foo {\n _Foo,\n foo\n }' To fix the problem rename enum entries to match the recommended naming conventions.", + "markdown": "Reports enum entry names that do not follow the recommended naming conventions.\n\n**Example:**\n\n\n enum class Foo {\n _Foo,\n foo\n }\n\nTo fix the problem rename enum entries to match the recommended naming conventions." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "EnumEntryName", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Naming conventions", + "index": 49, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryVariable", + "shortDescription": { + "text": "Unnecessary local variable" + }, + "fullDescription": { + "text": "Reports local variables that are used only in the very next 'return' statement or are exact copies of other variables. Such variables can be safely inlined to make the code more clear. Example: 'fun sum(a: Int, b: Int): Int {\n val c = a + b\n return c\n }' After the quick-fix is applied: 'fun sum(a: Int, b: Int): Int {\n return a + b\n }' Configure the inspection: Use the Report immediately returned variables option to report immediately returned variables. When given descriptive names, such variables may improve the code readability in some cases, that's why this option is disabled by default.", + "markdown": "Reports local variables that are used only in the very next `return` statement or are exact copies of other variables.\n\nSuch variables can be safely inlined to make the code more clear.\n\n**Example:**\n\n\n fun sum(a: Int, b: Int): Int {\n val c = a + b\n return c\n }\n\nAfter the quick-fix is applied:\n\n\n fun sum(a: Int, b: Int): Int {\n return a + b\n }\n\nConfigure the inspection:\n\nUse the **Report immediately returned variables** option to report immediately returned variables.\nWhen given descriptive names, such variables may improve the code readability in some cases, that's why this option is disabled by default." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "UnnecessaryVariable", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DeprecatedMavenDependency", + "shortDescription": { + "text": "Deprecated library is used in Maven" + }, + "fullDescription": { + "text": "Reports deprecated maven dependency. Example: '\n \n org.jetbrains.kotlin\n kotlin-stdlib-jre7\n ${kotlin.version}\n \n ' The quick fix changes the deprecated dependency to a maintained one: '\n \n org.jetbrains.kotlin\n kotlin-stdlib-jdk7\n ${kotlin.version}\n \n '", + "markdown": "Reports deprecated maven dependency.\n\n**Example:**\n\n\n \n \n org.jetbrains.kotlin\n kotlin-stdlib-jre7\n ${kotlin.version}\n \n \n\nThe quick fix changes the deprecated dependency to a maintained one:\n\n\n \n \n org.jetbrains.kotlin\n kotlin-stdlib-jdk7\n ${kotlin.version}\n \n \n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "DeprecatedMavenDependency", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin", + "index": 3, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceSubstringWithDropLast", + "shortDescription": { + "text": "'substring' call should be replaced with 'dropLast' call" + }, + "fullDescription": { + "text": "Reports calls like 's.substring(0, s.length - x)' that can be replaced with 's.dropLast(x)'. Using corresponding functions makes your code simpler. The quick-fix replaces the 'substring' call with 'dropLast'. Example: 'fun foo(s: String) {\n s.substring(0, s.length - 5)\n }' After the quick-fix is applied: 'fun foo(s: String) {\n s.dropLast(5)\n }'", + "markdown": "Reports calls like `s.substring(0, s.length - x)` that can be replaced with `s.dropLast(x)`.\n\nUsing corresponding functions makes your code simpler.\n\nThe quick-fix replaces the `substring` call with `dropLast`.\n\n**Example:**\n\n\n fun foo(s: String) {\n s.substring(0, s.length - 5)\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(s: String) {\n s.dropLast(5)\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceSubstringWithDropLast", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SetterBackingFieldAssignment", + "shortDescription": { + "text": "Existing backing field without assignment" + }, + "fullDescription": { + "text": "Reports property setters that don't update the backing field. The quick-fix adds an assignment to the backing field. Example: 'class Test {\n var foo: Int = 1\n set(value) {\n }\n }' After the quick-fix is applied: 'class Test {\n var foo: Int = 1\n set(value) {\n field = value\n }\n }'", + "markdown": "Reports property setters that don't update the backing field.\n\nThe quick-fix adds an assignment to the backing field.\n\n**Example:**\n\n\n class Test {\n var foo: Int = 1\n set(value) {\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Test {\n var foo: Int = 1\n set(value) {\n field = value\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SetterBackingFieldAssignment", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CopyWithoutNamedArguments", + "shortDescription": { + "text": "'copy' method of data class is called without named arguments" + }, + "fullDescription": { + "text": "Reports calls to a data class' 'copy()' method without named arguments. As all arguments of the 'copy()' function are optional, it might be hard to understand what properties are modified. Providing parameter names explicitly makes code easy to understand without navigating to the 'data class' declaration. Example: 'data class User(val name: String, val age: Int)\n\n fun copyUser(user: User): User {\n return user.copy(\"John\")\n }' The quick-fix provides parameter names to all 'copy()' arguments: 'data class User(val name: String, val age: Int)\n\n fun copyUser(user: User): User {\n return user.copy(name = \"John\")\n }'", + "markdown": "Reports calls to a data class' `copy()` method without named arguments.\n\n\nAs all arguments of the `copy()` function are optional, it might be hard to understand what properties are modified.\nProviding parameter names explicitly makes code easy to understand without navigating to the `data class` declaration.\n\n**Example:**\n\n\n data class User(val name: String, val age: Int)\n\n fun copyUser(user: User): User {\n return user.copy(\"John\")\n }\n\nThe quick-fix provides parameter names to all `copy()` arguments:\n\n\n data class User(val name: String, val age: Int)\n\n fun copyUser(user: User): User {\n return user.copy(name = \"John\")\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "CopyWithoutNamedArguments", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantConstructorKeyword", + "shortDescription": { + "text": "Redundant 'constructor' keyword" + }, + "fullDescription": { + "text": "Reports a redundant 'constructor' keyword on primary constructors. Example: 'class Foo constructor(x: Int, y: Int)' After the quick-fix is applied: 'class Foo(x: Int, y: Int)'", + "markdown": "Reports a redundant 'constructor' keyword on primary constructors.\n\n**Example:**\n\n\n class Foo constructor(x: Int, y: Int)\n\nAfter the quick-fix is applied:\n\n\n class Foo(x: Int, y: Int)\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RedundantConstructorKeyword", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KotlinDeprecation", + "shortDescription": { + "text": "Usage of redundant or deprecated syntax or deprecated symbols" + }, + "fullDescription": { + "text": "Reports obsolete language features and unnecessarily verbose code constructs during the code cleanup operation (Code | Code Cleanup). The quick-fix automatically replaces usages of obsolete language features or unnecessarily verbose code constructs with compact and up-to-date syntax. It also replaces deprecated symbols with their proposed substitutions.", + "markdown": "Reports obsolete language features and unnecessarily verbose code constructs during the code cleanup operation (**Code \\| Code Cleanup** ).\n\n\nThe quick-fix automatically replaces usages of obsolete language features or unnecessarily verbose code constructs with compact and up-to-date syntax.\n\n\nIt also replaces deprecated symbols with their proposed substitutions." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "KotlinDeprecation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Migration", + "index": 16, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantEmptyInitializerBlock", + "shortDescription": { + "text": "Redundant empty initializer block" + }, + "fullDescription": { + "text": "Reports redundant empty initializer blocks. Example: 'class Foo {\n init {\n // Empty init block\n }\n }' After the quick-fix is applied: 'class Foo {\n }'", + "markdown": "Reports redundant empty initializer blocks.\n\n**Example:**\n\n\n class Foo {\n init {\n // Empty init block\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo {\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "note", + "parameters": { + "suppressToolId": "RedundantEmptyInitializerBlock", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Other problems", + "index": 54, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceSubstringWithTake", + "shortDescription": { + "text": "'substring' call should be replaced with 'take' call" + }, + "fullDescription": { + "text": "Reports calls like 's.substring(0, x)' that can be replaced with 's.take(x)'. Using 'take()' makes your code simpler. The quick-fix replaces the 'substring' call with 'take()'. Example: 'fun foo(s: String) {\n s.substring(0, 10)\n }' After the quick-fix is applied: 'fun foo(s: String) {\n s.take(10)\n }'", + "markdown": "Reports calls like `s.substring(0, x)` that can be replaced with `s.take(x)`.\n\nUsing `take()` makes your code simpler.\n\nThe quick-fix replaces the `substring` call with `take()`.\n\n**Example:**\n\n\n fun foo(s: String) {\n s.substring(0, 10)\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(s: String) {\n s.take(10)\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceSubstringWithTake", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceRangeToWithRangeUntil", + "shortDescription": { + "text": "'rangeTo' or the '..' call should be replaced with '..<'" + }, + "fullDescription": { + "text": "Reports calls to 'rangeTo' or the '..' operator instead of calls to '..<'. Using corresponding functions makes your code simpler. The quick-fix replaces 'rangeTo' or the '..' call with '..<'. Example: 'fun foo(a: Int) {\n for (i in 0..a - 1) {\n\n }\n }' After the quick-fix is applied: 'fun foo(a: Int) {\n for (i in 0..) = lines\n .filter { it.isNotEmpty() }\n .map { it.split(',', limit = 2) }\n .filter { it.size == 2 }\n .map { Entity(it[0], it[1]) }' The quick-fix wraps call chain into 'asSequence()' and 'toList()': 'class Entity(val key: String, val value: String)\n\n fun getValues(lines: List) = lines\n .asSequence()\n .filter { it.isNotEmpty() }\n .map { it.split(',', limit = 2) }\n .filter { it.size == 2 }\n .map { Entity(it[0], it[1]) }\n .toList()'", + "markdown": "Reports call chain on a `Collection` that should be converted into **Sequence** .\n\nEach `Collection` transforming function (such as `map()` or `filter()`) creates a new\n`Collection` (typically `List` or `Set`) under the hood.\nIn case of multiple consequent calls, and a huge number of items in `Collection`, memory traffic might be significant.\nIn such a case, using `Sequence` is preferred.\n\n**Example:**\n\n\n class Entity(val key: String, val value: String)\n\n fun getValues(lines: List) = lines\n .filter { it.isNotEmpty() }\n .map { it.split(',', limit = 2) }\n .filter { it.size == 2 }\n .map { Entity(it[0], it[1]) }\n\nThe quick-fix wraps call chain into `asSequence()` and `toList()`:\n\n\n class Entity(val key: String, val value: String)\n\n fun getValues(lines: List) = lines\n .asSequence()\n .filter { it.isNotEmpty() }\n .map { it.split(',', limit = 2) }\n .filter { it.size == 2 }\n .map { Entity(it[0], it[1]) }\n .toList()\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ConvertCallChainIntoSequence", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AddOperatorModifier", + "shortDescription": { + "text": "Function should have 'operator' modifier" + }, + "fullDescription": { + "text": "Reports a function that matches one of the operator conventions but lacks the 'operator' keyword. By adding the 'operator' modifier, you might allow function consumers to write idiomatic Kotlin code. Example: 'class Complex(val real: Double, val imaginary: Double) {\n fun plus(other: Complex) =\n Complex(real + other.real, imaginary + other.imaginary)\n }\n\n fun usage(a: Complex, b: Complex) {\n a.plus(b)\n }' The quick-fix adds the 'operator' modifier keyword: 'class Complex(val real: Double, val imaginary: Double) {\n operator fun plus(other: Complex) =\n Complex(real + other.real, imaginary + other.imaginary)\n }\n\n fun usage(a: Complex, b: Complex) {\n a + b\n }'", + "markdown": "Reports a function that matches one of the operator conventions but lacks the `operator` keyword.\n\nBy adding the `operator` modifier, you might allow function consumers to write idiomatic Kotlin code.\n\n**Example:**\n\n\n class Complex(val real: Double, val imaginary: Double) {\n fun plus(other: Complex) =\n Complex(real + other.real, imaginary + other.imaginary)\n }\n\n fun usage(a: Complex, b: Complex) {\n a.plus(b)\n }\n\nThe quick-fix adds the `operator` modifier keyword:\n\n\n class Complex(val real: Double, val imaginary: Double) {\n operator fun plus(other: Complex) =\n Complex(real + other.real, imaginary + other.imaginary)\n }\n\n fun usage(a: Complex, b: Complex) {\n a + b\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "AddOperatorModifier", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MayBeConstant", + "shortDescription": { + "text": "Might be 'const'" + }, + "fullDescription": { + "text": "Reports top-level 'val' properties in objects that might be declared as 'const' for better performance and Java interoperability. Example: 'object A {\n val foo = 1\n }' After the quick-fix is applied: 'object A {\n const val foo = 1\n }'", + "markdown": "Reports top-level `val` properties in objects that might be declared as `const` for better performance and Java interoperability.\n\n**Example:**\n\n\n object A {\n val foo = 1\n }\n\nAfter the quick-fix is applied:\n\n\n object A {\n const val foo = 1\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "MayBeConstant", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GradleKotlinxCoroutinesDeprecation", + "shortDescription": { + "text": "Incompatible kotlinx.coroutines dependency is used with Kotlin 1.3+ in Gradle" + }, + "fullDescription": { + "text": "Reports 'kotlinx.coroutines' library dependencies in Gradle that should be updated to be compatible with Kotlin 1.3+. Example: 'dependencies {\n implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.0.1'\n }' The quick fix changes the 'kotlinx.coroutines' library version to a compatible with Kotlin 1.3: 'dependencies {\n implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.27.0-eap13'\n }'", + "markdown": "Reports `kotlinx.coroutines` library dependencies in Gradle that should be updated to be compatible with Kotlin 1.3+.\n\n**Example:**\n\n\n dependencies {\n implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.0.1'\n }\n\nThe quick fix changes the `kotlinx.coroutines` library version to a compatible with Kotlin 1.3:\n\n\n dependencies {\n implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.27.0-eap13'\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "GradleKotlinxCoroutinesDeprecation", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Migration/Gradle", + "index": 114, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "WarningOnMainUnusedParameterMigration", + "shortDescription": { + "text": "Unused 'args' on 'main' since 1.4" + }, + "fullDescription": { + "text": "Reports 'main' function with an unused single parameter. Since Kotlin 1.4, it is possible to use the 'main' function without parameter as the entry point to the Kotlin program. The compiler reports a warning for the 'main' function with an unused parameter.", + "markdown": "Reports `main` function with an unused single parameter.\n\nSince Kotlin 1.4, it is possible to use the `main` function without parameter as the entry point to the Kotlin program.\nThe compiler reports a warning for the `main` function with an unused parameter." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "WarningOnMainUnusedParameterMigration", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Migration", + "index": 16, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantWith", + "shortDescription": { + "text": "Redundant 'with' call" + }, + "fullDescription": { + "text": "Reports redundant 'with' function calls that don't access anything from the receiver. Examples: 'class MyClass {\n fun f(): String = \"\"\n }\n\n fun testRedundant() {\n with(c) { // <== 'with' is redundant since 'c' isn't used\n println(\"1\")\n }\n }\n\n fun testOk() {\n val c = MyClass()\n with(c) { // <== OK because 'f()' is effectively 'c.f()'\n println(f())\n }\n }'", + "markdown": "Reports redundant `with` function calls that don't access anything from the receiver.\n\n**Examples:**\n\n\n class MyClass {\n fun f(): String = \"\"\n }\n\n fun testRedundant() {\n with(c) { // <== 'with' is redundant since 'c' isn't used\n println(\"1\")\n }\n }\n\n fun testOk() {\n val c = MyClass()\n with(c) { // <== OK because 'f()' is effectively 'c.f()'\n println(f())\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RedundantWith", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceIsEmptyWithIfEmpty", + "shortDescription": { + "text": "'if' condition can be replaced with lambda call" + }, + "fullDescription": { + "text": "Reports 'isEmpty', 'isBlank', 'isNotEmpty', or 'isNotBlank' calls in an 'if' statement to assign a default value. The quick-fix replaces the 'if' condition with 'ifEmpty' or 'ifBlank' calls. Example: 'fun test(list: List): List {\n return if (list.isEmpty()) {\n println()\n foo()\n } else {\n list\n }\n }' After the quick-fix is applied: 'fun test(list: List): List {\n return list.ifEmpty {\n println()\n foo()\n }\n }' This inspection only reports if the Kotlin language version of the project or module is 1.3 or higher.", + "markdown": "Reports `isEmpty`, `isBlank`, `isNotEmpty`, or `isNotBlank` calls in an `if` statement to assign a default value.\n\nThe quick-fix replaces the `if` condition with `ifEmpty` or `ifBlank` calls.\n\n**Example:**\n\n\n fun test(list: List): List {\n return if (list.isEmpty()) {\n println()\n foo()\n } else {\n list\n }\n }\n\nAfter the quick-fix is applied:\n\n\n fun test(list: List): List {\n return list.ifEmpty {\n println()\n foo()\n }\n }\n\nThis inspection only reports if the Kotlin language version of the project or module is 1.3 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceIsEmptyWithIfEmpty", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantLabelMigration", + "shortDescription": { + "text": "Redundant label" + }, + "fullDescription": { + "text": "Reports redundant labels which cause compilation errors since Kotlin 1.4. Since Kotlin 1.0, one can mark any statement with a label: 'fun foo() {\n L1@ val x = L2@bar()\n }' However, these labels can be referenced only in a limited number of ways: break / continue from a loop non-local return from an inline lambda or inline anonymous function sssss Such labels are prohibited since Kotlin 1.4. This inspection only reports if the Kotlin language level of the project or module is 1.4 or higher.", + "markdown": "Reports redundant labels which cause compilation errors since Kotlin 1.4.\n\nSince Kotlin 1.0, one can mark any statement with a label:\n\n\n fun foo() {\n L1@ val x = L2@bar()\n }\n\nHowever, these labels can be referenced only in a limited number of ways:\n\n* break / continue from a loop\n* non-local return from an inline lambda or inline anonymous function\nsssss\n\nSuch labels are prohibited since Kotlin 1.4.\n\nThis inspection only reports if the Kotlin language level of the project or module is 1.4 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RedundantLabelMigration", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Migration", + "index": 16, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KotlinRedundantDiagnosticSuppress", + "shortDescription": { + "text": "Redundant diagnostic suppression" + }, + "fullDescription": { + "text": "Reports usages of '@Suppress' annotations that can be safely removed because the compiler diagnostic they affect is no longer applicable in this context. Example: 'fun doSmth(@Suppress(\"UNUSED_PARAMETER\") used: Int) {\n println(used)\n }' After the quick-fix is applied: 'fun doSmth(used: Int) {\n println(used)\n }'", + "markdown": "Reports usages of `@Suppress` annotations that can be safely removed because the compiler diagnostic they affect is no longer applicable in this context.\n\n**Example:**\n\n\n fun doSmth(@Suppress(\"UNUSED_PARAMETER\") used: Int) {\n println(used)\n }\n\nAfter the quick-fix is applied:\n\n\n fun doSmth(used: Int) {\n println(used)\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "KotlinRedundantDiagnosticSuppress", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceWithImportAlias", + "shortDescription": { + "text": "Fully qualified name can be replaced with existing import alias" + }, + "fullDescription": { + "text": "Reports fully qualified names that can be replaced with an existing import alias. Example: 'import foo.Foo as Bar\nfun main() {\n foo.Foo()\n}' After the quick-fix is applied: 'import foo.Foo as Bar\nfun main() {\n Bar()\n}'", + "markdown": "Reports fully qualified names that can be replaced with an existing import alias.\n\n**Example:**\n\n\n import foo.Foo as Bar\n fun main() {\n foo.Foo()\n }\n\nAfter the quick-fix is applied:\n\n\n import foo.Foo as Bar\n fun main() {\n Bar()\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceWithImportAlias", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceNegatedIsEmptyWithIsNotEmpty", + "shortDescription": { + "text": "Negated call can be simplified" + }, + "fullDescription": { + "text": "Reports negation 'isEmpty()' and 'isNotEmpty()' for collections and 'String', or 'isBlank()' and 'isNotBlank()' for 'String'. Using corresponding functions makes your code simpler. The quick-fix replaces the negation call with the corresponding call from the Standard Library. Example: 'fun main() {\n val list = listOf(1,2,3)\n if (!list.isEmpty()) {\n // do smth\n }\n }' After the quick-fix is applied: 'fun main() {\n val list = listOf(1,2,3)\n if (list.isNotEmpty()) {\n // do smth\n }\n }'", + "markdown": "Reports negation `isEmpty()` and `isNotEmpty()` for collections and `String`, or `isBlank()` and `isNotBlank()` for `String`.\n\nUsing corresponding functions makes your code simpler.\n\nThe quick-fix replaces the negation call with the corresponding call from the Standard Library.\n\n**Example:**\n\n\n fun main() {\n val list = listOf(1,2,3)\n if (!list.isEmpty()) {\n // do smth\n }\n }\n\nAfter the quick-fix is applied:\n\n\n fun main() {\n val list = listOf(1,2,3)\n if (list.isNotEmpty()) {\n // do smth\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceNegatedIsEmptyWithIsNotEmpty", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DelegationToVarProperty", + "shortDescription": { + "text": "Delegating to 'var' property" + }, + "fullDescription": { + "text": "Reports interface delegation to a 'var' property. Only initial value of a property is used for delegation, any later assignments do not affect it. Example: 'class Example(var text: CharSequence): CharSequence by text' The quick-fix replaces a property with immutable one: 'class Example(val text: CharSequence): CharSequence by text' Alternative way, if you rely on mutability for some reason: 'class Example(text: CharSequence): CharSequence by text {\n var text = text\n }'", + "markdown": "Reports interface delegation to a `var` property.\n\nOnly initial value of a property is used for delegation, any later assignments do not affect it.\n\n**Example:**\n\n\n class Example(var text: CharSequence): CharSequence by text\n\nThe quick-fix replaces a property with immutable one:\n\n\n class Example(val text: CharSequence): CharSequence by text\n\nAlternative way, if you rely on mutability for some reason:\n\n\n class Example(text: CharSequence): CharSequence by text {\n var text = text\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "DelegationToVarProperty", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConstantConditionIf", + "shortDescription": { + "text": "Condition of 'if' expression is constant" + }, + "fullDescription": { + "text": "Reports 'if' expressions that have 'true' or 'false' constant literal condition and can be simplified. While occasionally intended, this construction is confusing and often the result of a typo or previous refactoring. Example: 'fun example() {\n if (true) {\n throw IllegalStateException(\"Unexpected state\")\n }\n }' The quick-fix removes the 'if' condition: 'fun example() {\n throw IllegalStateException(\"Unexpected state\")\n }'", + "markdown": "Reports `if` expressions that have `true` or `false` constant literal condition and can be simplified.\n\nWhile occasionally intended, this construction is confusing and often the result of a typo\nor previous refactoring.\n\n**Example:**\n\n\n fun example() {\n if (true) {\n throw IllegalStateException(\"Unexpected state\")\n }\n }\n\nThe quick-fix removes the `if` condition:\n\n\n fun example() {\n throw IllegalStateException(\"Unexpected state\")\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ConstantConditionIf", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantLambdaArrow", + "shortDescription": { + "text": "Redundant lambda arrow" + }, + "fullDescription": { + "text": "Reports redundant lambda arrows in lambdas without parameters. Example: 'fun foo(f: () -> Unit) = f()\n\n fun bar() {\n foo { -> println(\"Hi!\") }\n }' After the quick-fix is applied: 'fun foo(f: () -> Unit) = f()\n\n fun bar() {\n foo { println(\"Hi!\") }\n }'", + "markdown": "Reports redundant lambda arrows in lambdas without parameters.\n\n**Example:**\n\n\n fun foo(f: () -> Unit) = f()\n\n fun bar() {\n foo { -> println(\"Hi!\") }\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(f: () -> Unit) = f()\n\n fun bar() {\n foo { println(\"Hi!\") }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RedundantLambdaArrow", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KotlinInternalInJava", + "shortDescription": { + "text": "Usage of Kotlin internal declarations from Java" + }, + "fullDescription": { + "text": "Reports usages of Kotlin 'internal' declarations in Java code that is located in a different module. The 'internal' keyword is designed to restrict access to a class, function, or property from other modules. Due to JVM limitations, 'internal' classes, functions, and properties can still be accessed from outside Kotlin, which may later lead to compatibility problems.", + "markdown": "Reports usages of Kotlin `internal` declarations in Java code that is located in a different module.\n\n\nThe `internal` keyword is designed to restrict access to a class, function, or property from other modules.\nDue to JVM limitations, `internal` classes, functions, and properties can still be\naccessed from outside Kotlin, which may later lead to compatibility problems." + }, + "defaultConfiguration": { + "enabled": true, + "level": "error", + "parameters": { + "suppressToolId": "KotlinInternalInJava", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Java interop issues", + "index": 70, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SimplifyBooleanWithConstants", + "shortDescription": { + "text": "Boolean expression can be simplified" + }, + "fullDescription": { + "text": "Reports boolean expression parts that can be reduced to constants. The quick-fix simplifies the condition. Example: 'fun use(arg: Boolean) {\n if (false == arg) {\n\n }\n }' After the quick-fix is applied: 'fun use(arg: Boolean) {\n if (!arg) {\n\n }\n }'", + "markdown": "Reports boolean expression parts that can be reduced to constants.\n\nThe quick-fix simplifies the condition.\n\n**Example:**\n\n\n fun use(arg: Boolean) {\n if (false == arg) {\n\n }\n }\n\nAfter the quick-fix is applied:\n\n\n fun use(arg: Boolean) {\n if (!arg) {\n\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "SimplifyBooleanWithConstants", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OverrideDeprecatedMigration", + "shortDescription": { + "text": "Do not propagate method deprecation through overrides since 1.9" + }, + "fullDescription": { + "text": "Reports a declarations that are propagated by '@Deprecated' annotation that will lead to compilation error since 1.9. Motivation types: Implementation changes are required for implementation design/architectural reasons Inconsistency in the design (things are done differently in different contexts) More details: KT-47902: Do not propagate method deprecation through overrides The quick-fix copies '@Deprecated' annotation from the parent declaration. Example: 'open class Base {\n @Deprecated(\"Don't use\")\n open fun foo() {}\n }\n\n class Derived : Base() {\n override fun foo() {}\n }' After the quick-fix is applied: 'open class Base {\n @Deprecated(\"Don't use\")\n open fun foo() {}\n }\n\n class Derived : Base() {\n @Deprecated(\"Don't use\")\n override fun foo() {}\n }' This inspection only reports if the Kotlin language level of the project or module is 1.6 or higher.", + "markdown": "Reports a declarations that are propagated by `@Deprecated` annotation that will lead to compilation error since 1.9.\n\nMotivation types:\n\n* Implementation changes are required for implementation design/architectural reasons\n* Inconsistency in the design (things are done differently in different contexts)\n\n**More details:** [KT-47902: Do not propagate method deprecation through overrides](https://youtrack.jetbrains.com/issue/KT-47902)\n\nThe quick-fix copies `@Deprecated` annotation from the parent declaration.\n\n**Example:**\n\n\n open class Base {\n @Deprecated(\"Don't use\")\n open fun foo() {}\n }\n\n class Derived : Base() {\n override fun foo() {}\n }\n\nAfter the quick-fix is applied:\n\n\n open class Base {\n @Deprecated(\"Don't use\")\n open fun foo() {}\n }\n\n class Derived : Base() {\n @Deprecated(\"Don't use\")\n override fun foo() {}\n }\n\nThis inspection only reports if the Kotlin language level of the project or module is 1.6 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "OverrideDeprecatedMigration", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Migration", + "index": 16, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NoConstructorMigration", + "shortDescription": { + "text": "Forbidden constructor call" + }, + "fullDescription": { + "text": "Reports a constructor calls on functional supertypes that will lead to compilation error since 1.9. Motivation types: The implementation does not abide by a published spec or documentation More details: KT-46344: No error for a super class constructor call on a function interface in supertypes list The quick-fix removes a constructor call. Example: 'abstract class A : () -> Int()' After the quick-fix is applied: 'abstract class A : () -> Int' This inspection only reports if the Kotlin language level of the project or module is 1.7 or higher.", + "markdown": "Reports a constructor calls on functional supertypes that will lead to compilation error since 1.9.\n\nMotivation types:\n\n* The implementation does not abide by a published spec or documentation\n\n**More details:** [KT-46344: No error for a super class constructor call on a function interface in supertypes list](https://youtrack.jetbrains.com/issue/KT-46344)\n\nThe quick-fix removes a constructor call.\n\n**Example:**\n\n\n abstract class A : () -> Int()\n\nAfter the quick-fix is applied:\n\n\n abstract class A : () -> Int\n\nThis inspection only reports if the Kotlin language level of the project or module is 1.7 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "NoConstructorMigration", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Migration", + "index": 16, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantModalityModifier", + "shortDescription": { + "text": "Redundant modality modifier" + }, + "fullDescription": { + "text": "Reports the modality modifiers that match the default modality of an element ('final' for most elements, 'open' for members with an 'override'). Example: 'final class Foo\n\n open class Bar : Comparable {\n open override fun compareTo(other: Bar): Int = 0\n }' After the quick-fix is applied: 'class Foo\n\n open class Bar : Comparable {\n override fun compareTo(other: Bar): Int = 0\n }'", + "markdown": "Reports the modality modifiers that match the default modality of an element (`final` for most elements, `open` for members with an `override`).\n\n**Example:**\n\n\n final class Foo\n\n open class Bar : Comparable {\n open override fun compareTo(other: Bar): Int = 0\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo\n\n open class Bar : Comparable {\n override fun compareTo(other: Bar): Int = 0\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantModalityModifier", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantValueArgument", + "shortDescription": { + "text": "Redundant value argument" + }, + "fullDescription": { + "text": "Reports arguments that are equal to the default values of the corresponding parameters. Example: 'fun foo(x: Int, y: Int = 2) {}\n\nfun bar() {\n foo(1, 2)\n}' After the quick-fix is applied: 'fun bar() {\n foo(1)\n}'", + "markdown": "Reports arguments that are equal to the default values of the corresponding parameters.\n\n**Example:**\n\n\n fun foo(x: Int, y: Int = 2) {}\n\n fun bar() {\n foo(1, 2)\n }\n\nAfter the quick-fix is applied:\n\n\n fun bar() {\n foo(1)\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RedundantValueArgument", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UseWithIndex", + "shortDescription": { + "text": "Manually incremented index variable can be replaced with use of 'withIndex()'" + }, + "fullDescription": { + "text": "Reports 'for' loops with a manually incremented index variable. 'for' loops with a manually incremented index variable can be simplified with the 'withIndex()' function. Use withIndex() instead of manual index increment quick-fix can be used to amend the code automatically. Example: 'fun foo(list: List): Int? {\n var index = 0\n for (s in list) { <== can be simplified\n val x = s.length * index\n index++\n if (x > 0) return x\n }\n return null\n }' After the quick-fix is applied: 'fun foo(list: List): Int? {\n for ((index, s) in list.withIndex()) {\n val x = s.length * index\n if (x > 0) return x\n }\n return null\n }'", + "markdown": "Reports `for` loops with a manually incremented index variable.\n\n`for` loops with a manually incremented index variable can be simplified with the `withIndex()` function.\n\n**Use withIndex() instead of manual index increment** quick-fix can be used to amend the code automatically.\n\nExample:\n\n\n fun foo(list: List): Int? {\n var index = 0\n for (s in list) { <== can be simplified\n val x = s.length * index\n index++\n if (x > 0) return x\n }\n return null\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(list: List): Int? {\n for ((index, s) in list.withIndex()) {\n val x = s.length * index\n if (x > 0) return x\n }\n return null\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "UseWithIndex", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SimplifyAssertNotNull", + "shortDescription": { + "text": "'assert' call can be replaced with '!!' or '?:'" + }, + "fullDescription": { + "text": "Reports 'assert' calls that check a not null value of the declared variable. Using '!!' or '?:' makes your code simpler. The quick-fix replaces 'assert' with '!!' or '?:' operator in the variable initializer. Example: 'fun foo(p: Array) {\n val v = p[0]\n assert(v != null, { \"Should be not null\" })\n }' After the quick-fix is applied: 'fun foo(p: Array) {\n val v = p[0] ?: error(\"Should be not null\")\n }'", + "markdown": "Reports `assert` calls that check a not null value of the declared variable.\n\nUsing `!!` or `?:` makes your code simpler.\n\nThe quick-fix replaces `assert` with `!!` or `?:` operator in the variable initializer.\n\n**Example:**\n\n\n fun foo(p: Array) {\n val v = p[0]\n assert(v != null, { \"Should be not null\" })\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(p: Array) {\n val v = p[0] ?: error(\"Should be not null\")\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "SimplifyAssertNotNull", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConvertNaNEquality", + "shortDescription": { + "text": "Convert equality check with 'NaN' to 'isNaN' call" + }, + "fullDescription": { + "text": "Reports an equality check with 'Float.NaN' or 'Double.NaN' that should be replaced with an 'isNaN()' check. According to IEEE 754, equality check against NaN always returns 'false', even for 'NaN == NaN'. Therefore, such a check is likely to be a mistake. The quick-fix replaces comparison with 'isNaN()' check that uses a different comparison technique and handles 'NaN' values correctly. Example: 'fun check(value: Double): Boolean {\n return Double.NaN == value\n }' After the fix is applied: 'fun check(value: Double): Boolean {\n return value.isNaN()\n }'", + "markdown": "Reports an equality check with `Float.NaN` or `Double.NaN` that should be replaced with an `isNaN()` check.\n\n\nAccording to IEEE 754, equality check against NaN always returns `false`, even for `NaN == NaN`.\nTherefore, such a check is likely to be a mistake.\n\nThe quick-fix replaces comparison with `isNaN()` check that uses a different comparison technique and handles `NaN` values correctly.\n\n**Example:**\n\n\n fun check(value: Double): Boolean {\n return Double.NaN == value\n }\n\nAfter the fix is applied:\n\n\n fun check(value: Double): Boolean {\n return value.isNaN()\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ConvertNaNEquality", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceManualRangeWithIndicesCalls", + "shortDescription": { + "text": "Range can be converted to indices or iteration" + }, + "fullDescription": { + "text": "Reports 'until' and 'rangeTo' operators that can be replaced with 'Collection.indices' or iteration over collection inside 'for' loop. Using syntactic sugar makes your code simpler. The quick-fix replaces the manual range with the corresponding construction. Example: 'fun main(args: Array) {\n for (index in 0..args.size - 1) {\n println(args[index])\n }\n }' After the quick-fix is applied: 'fun main(args: Array) {\n for (element in args) {\n println(element)\n }\n }'", + "markdown": "Reports `until` and `rangeTo` operators that can be replaced with `Collection.indices` or iteration over collection inside `for` loop.\n\nUsing syntactic sugar makes your code simpler.\n\nThe quick-fix replaces the manual range with the corresponding construction.\n\n**Example:**\n\n\n fun main(args: Array) {\n for (index in 0..args.size - 1) {\n println(args[index])\n }\n }\n\nAfter the quick-fix is applied:\n\n\n fun main(args: Array) {\n for (element in args) {\n println(element)\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceManualRangeWithIndicesCalls", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KotlinLoggerInitializedWithForeignClass", + "shortDescription": { + "text": "Logger initialized with foreign class" + }, + "fullDescription": { + "text": "Reports 'Logger' instances initialized with a class literal other than the class the 'Logger' resides in. This can happen when copy-pasting from another class. It may result in logging events under an unexpected category and incorrect filtering. Use the inspection options to specify the logger factory classes and methods recognized by this inspection. Example: 'class AnotherService\nclass MyService {\n private val logger = LoggerFactory.getLogger(AnotherService::class.java)\n}' After the quick-fix is applied: 'class MyService {\n private val logger = LoggerFactory.getLogger(MyService::class.java)\n}'", + "markdown": "Reports `Logger` instances initialized with a class literal other than the class the `Logger` resides in.\n\n\nThis can happen when copy-pasting from another class.\nIt may result in logging events under an unexpected category and incorrect filtering.\n\n\nUse the inspection options to specify the logger factory classes and methods recognized by this inspection.\n\n**Example:**\n\n\n class AnotherService\n class MyService {\n private val logger = LoggerFactory.getLogger(AnotherService::class.java)\n }\n\nAfter the quick-fix is applied:\n\n\n class MyService {\n private val logger = LoggerFactory.getLogger(MyService::class.java)\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "KotlinLoggerInitializedWithForeignClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Logging", + "index": 116, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ImplicitThis", + "shortDescription": { + "text": "Implicit 'this'" + }, + "fullDescription": { + "text": "Reports usages of implicit this. Example: 'class Foo {\n fun s() = \"\"\n\n fun test() {\n s()\n }\n }' The quick fix specifies this explicitly: 'class Foo {\n fun s() = \"\"\n\n fun test() {\n this.s()\n }\n }'", + "markdown": "Reports usages of implicit **this** .\n\n**Example:**\n\n\n class Foo {\n fun s() = \"\"\n\n fun test() {\n s()\n }\n }\n\nThe quick fix specifies **this** explicitly:\n\n\n class Foo {\n fun s() = \"\"\n\n fun test() {\n this.s()\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ImplicitThis", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantAsSequence", + "shortDescription": { + "text": "Redundant 'asSequence' call" + }, + "fullDescription": { + "text": "Reports redundant 'asSequence()' call that can never have a positive performance effect. 'asSequence()' speeds up collection processing that includes multiple operations because it performs operations lazily and doesn't create intermediate collections. However, if a terminal operation (such as 'toList()') is used right after 'asSequence()', this doesn't give you any positive performance effect. Example: 'fun test(list: List) {\n list.asSequence().last()\n }' After the quick-fix is applied: 'fun test(list: List) {\n list.last()\n }'", + "markdown": "Reports redundant `asSequence()` call that can never have a positive performance effect.\n\n\n`asSequence()` speeds up collection processing that includes multiple operations because it performs operations lazily\nand doesn't create intermediate collections.\n\n\nHowever, if a terminal operation (such as `toList()`) is used right after `asSequence()`, this doesn't give\nyou any positive performance effect.\n\n**Example:**\n\n\n fun test(list: List) {\n list.asSequence().last()\n }\n\nAfter the quick-fix is applied:\n\n\n fun test(list: List) {\n list.last()\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RedundantAsSequence", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RemoveToStringInStringTemplate", + "shortDescription": { + "text": "Redundant call to 'toString()' in string template" + }, + "fullDescription": { + "text": "Reports calls to 'toString()' in string templates that can be safely removed. Example: 'fun foo(a: Int, b: Int) = a + b\n\n fun test(): String {\n return \"Foo: ${foo(0, 4).toString()}\" // 'toString()' is redundant\n }' After the quick-fix is applied: 'fun foo(a: Int, b: Int) = a + b\n\n fun test(): String {\n return \"Foo: ${foo(0, 4)}\"\n }'", + "markdown": "Reports calls to `toString()` in string templates that can be safely removed.\n\n**Example:**\n\n fun foo(a: Int, b: Int) = a + b\n\n fun test(): String {\n return \"Foo: ${foo(0, 4).toString()}\" // 'toString()' is redundant\n }\n\nAfter the quick-fix is applied:\n\n fun foo(a: Int, b: Int) = a + b\n\n fun test(): String {\n return \"Foo: ${foo(0, 4)}\"\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RemoveToStringInStringTemplate", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KotlinCatchMayIgnoreException", + "shortDescription": { + "text": "'catch' block may ignore exception" + }, + "fullDescription": { + "text": "Reports 'catch' blocks that are empty or may ignore an exception. While occasionally intended, empty 'catch' blocks may complicate debugging. Also, ignoring a 'catch' parameter might be wrong. The inspection won't report any 'catch' parameters named 'ignore', 'ignored', or '_'. You can use the quick-fix to change the exception name to '_'. Example: 'try {\n throwingMethod()\n } catch (ex: IOException) {\n\n }' After the quick-fix is applied: 'try {\n throwingMethod()\n } catch (_: IOException) {\n\n }' Use the Do not warn when 'catch' block contains a comment option to ignore 'catch' blocks with comments.", + "markdown": "Reports `catch` blocks that are empty or may ignore an exception.\n\nWhile occasionally intended, empty `catch` blocks may complicate debugging.\nAlso, ignoring a `catch` parameter might be wrong.\n\n\nThe inspection won't report any `catch` parameters named `ignore`, `ignored`, or `_`.\n\n\nYou can use the quick-fix to change the exception name to `_`.\n\n**Example:**\n\n\n try {\n throwingMethod()\n } catch (ex: IOException) {\n\n }\n\nAfter the quick-fix is applied:\n\n\n try {\n throwingMethod()\n } catch (_: IOException) {\n\n }\n\nUse the **Do not warn when 'catch' block contains a comment** option to ignore `catch` blocks with comments." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "CatchMayIgnoreException", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DifferentStdlibGradleVersion", + "shortDescription": { + "text": "Kotlin library and Gradle plugin versions are different" + }, + "fullDescription": { + "text": "Reports different Kotlin stdlib and compiler versions. Example: 'dependencies {\n classpath \"org.jetbrains.kotlin:kotlin-stdlib:0.0.1\"\n }' To fix the problem change the kotlin stdlib version to match the kotlin compiler version.", + "markdown": "Reports different Kotlin stdlib and compiler versions.\n\n**Example:**\n\n\n dependencies {\n classpath \"org.jetbrains.kotlin:kotlin-stdlib:0.0.1\"\n }\n\nTo fix the problem change the kotlin stdlib version to match the kotlin compiler version." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "DifferentStdlibGradleVersion", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin", + "index": 3, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KotlinUnusedImport", + "shortDescription": { + "text": "Unused import directive" + }, + "fullDescription": { + "text": "Reports redundant 'import' statements. Default and unused imports can be safely removed. Example: 'import kotlin.*\n import kotlin.collections.*\n import kotlin.comparisons.*\n import kotlin.io.*\n import kotlin.ranges.*\n import kotlin.sequences.*\n import kotlin.text.*\n\n // jvm specific\n import java.lang.*\n import kotlin.jvm.*\n\n // js specific\n import kotlin.js.*\n\n import java.io.* // this import is unused and could be removed\n import java.util.*\n\n fun foo(list: ArrayList) {\n list.add(\"\")\n }'", + "markdown": "Reports redundant `import` statements.\n\nDefault and unused imports can be safely removed.\n\n**Example:**\n\n\n import kotlin.*\n import kotlin.collections.*\n import kotlin.comparisons.*\n import kotlin.io.*\n import kotlin.ranges.*\n import kotlin.sequences.*\n import kotlin.text.*\n\n // jvm specific\n import java.lang.*\n import kotlin.jvm.*\n\n // js specific\n import kotlin.js.*\n\n import java.io.* // this import is unused and could be removed\n import java.util.*\n\n fun foo(list: ArrayList) {\n list.add(\"\")\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnusedImport", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CanBePrimaryConstructorProperty", + "shortDescription": { + "text": "Property is explicitly assigned to constructor parameter" + }, + "fullDescription": { + "text": "Reports properties that are explicitly assigned to primary constructor parameters. Properties can be declared directly in the primary constructor, reducing the amount of code and increasing code readability. Example: 'class User(name: String) {\n val name = name\n }' The quick-fix joins the parameter and property declaration into a primary constructor parameter: 'class User(val name: String) {\n }'", + "markdown": "Reports properties that are explicitly assigned to primary constructor parameters.\n\nProperties can be declared directly in the primary constructor, reducing the amount of code and increasing code readability.\n\n**Example:**\n\n\n class User(name: String) {\n val name = name\n }\n\nThe quick-fix joins the parameter and property declaration into a primary constructor parameter:\n\n\n class User(val name: String) {\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CanBePrimaryConstructorProperty", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CanBeVal", + "shortDescription": { + "text": "Local 'var' is never modified and can be declared as 'val'" + }, + "fullDescription": { + "text": "Reports local variables declared with the 'var' keyword that are never modified. Kotlin encourages to declare practically immutable variables using the 'val' keyword, ensuring that their value will never change. Example: 'fun example() {\n var primeNumbers = listOf(1, 2, 3, 5, 7, 11, 13)\n var fibonacciNumbers = listOf(1, 1, 2, 3, 5, 8, 13)\n print(\"Same numbers: \" + primeNumbers.intersect(fibonacciNumbers))\n }' The quick-fix replaces the 'var' keyword with 'val': 'fun example() {\n val primeNumbers = listOf(1, 2, 3, 5, 7, 11, 13)\n val fibonacciNumbers = listOf(1, 1, 2, 3, 5, 8, 13)\n print(\"Same numbers: \" + primeNumbers.intersect(fibonacciNumbers))\n }'", + "markdown": "Reports local variables declared with the `var` keyword that are never modified.\n\nKotlin encourages to declare practically immutable variables using the `val` keyword, ensuring that their value will never change.\n\n**Example:**\n\n\n fun example() {\n var primeNumbers = listOf(1, 2, 3, 5, 7, 11, 13)\n var fibonacciNumbers = listOf(1, 1, 2, 3, 5, 8, 13)\n print(\"Same numbers: \" + primeNumbers.intersect(fibonacciNumbers))\n }\n\nThe quick-fix replaces the `var` keyword with `val`:\n\n\n fun example() {\n val primeNumbers = listOf(1, 2, 3, 5, 7, 11, 13)\n val fibonacciNumbers = listOf(1, 1, 2, 3, 5, 8, 13)\n print(\"Same numbers: \" + primeNumbers.intersect(fibonacciNumbers))\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CanBeVal", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JavaMapForEach", + "shortDescription": { + "text": "Java Map.forEach method call should be replaced with Kotlin's forEach" + }, + "fullDescription": { + "text": "Reports a Java Map.'forEach' method call that can be replaced with Kotlin's forEach. Example: 'fun test(map: HashMap) {\n map.forEach { key, value ->\n foo(key, value)\n }\n }\n\n fun foo(i: Int, s: String) {}' The quick-fix adds parentheses: 'fun test(map: HashMap) {\n map.forEach { (key, value) ->\n foo(key, value)\n }\n }\n\n fun foo(i: Int, s: String) {}'", + "markdown": "Reports a Java Map.`forEach` method call that can be replaced with Kotlin's **forEach** .\n\n**Example:**\n\n\n fun test(map: HashMap) {\n map.forEach { key, value ->\n foo(key, value)\n }\n }\n\n fun foo(i: Int, s: String) {}\n\nThe quick-fix adds parentheses:\n\n\n fun test(map: HashMap) {\n map.forEach { (key, value) ->\n foo(key, value)\n }\n }\n\n fun foo(i: Int, s: String) {}\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "JavaMapForEach", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantObjectTypeCheck", + "shortDescription": { + "text": "Non-idiomatic 'is' type check for an object" + }, + "fullDescription": { + "text": "Reports non-idiomatic 'is' type checks for an object. It's recommended to replace such checks with reference comparison. Example: 'object Foo\n\n fun foo(arg: Any) = when {\n arg is Foo -> ...\n arg !is Foo -> ...\n }' After the quick-fix is applied: 'object Foo\n\n fun foo(arg: Any) = when {\n arg === Foo -> ...\n arg !== Foo -> ...\n }'", + "markdown": "Reports non-idiomatic `is` type checks for an object.\n\nIt's recommended to replace such checks with reference comparison.\n\n**Example:**\n\n\n object Foo\n\n fun foo(arg: Any) = when {\n arg is Foo -> ...\n arg !is Foo -> ...\n }\n\nAfter the quick-fix is applied:\n\n\n object Foo\n\n fun foo(arg: Any) = when {\n arg === Foo -> ...\n arg !== Foo -> ...\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RedundantObjectTypeCheck", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspendFunctionOnCoroutineScope", + "shortDescription": { + "text": "Ambiguous coroutineContext due to CoroutineScope receiver of suspend function" + }, + "fullDescription": { + "text": "Reports calls and accesses of 'CoroutineScope' extensions or members inside suspend functions with 'CoroutineScope' receiver. When a function is 'suspend' and has 'CoroutineScope' receiver, it has ambiguous access to 'CoroutineContext' via 'kotlin.coroutines.coroutineContext' and via 'CoroutineScope.coroutineContext', and two these contexts are different in general. To improve this situation, one can wrap suspicious call inside 'coroutineScope { ... }' or get rid of 'CoroutineScope' function receiver.", + "markdown": "Reports calls and accesses of `CoroutineScope` extensions or members inside suspend functions with `CoroutineScope` receiver.\n\nWhen a function is `suspend` and has `CoroutineScope` receiver,\nit has ambiguous access to `CoroutineContext` via `kotlin.coroutines.coroutineContext` and via `CoroutineScope.coroutineContext`,\nand two these contexts are different in general.\n\n\nTo improve this situation, one can wrap suspicious call inside `coroutineScope { ... }` or\nget rid of `CoroutineScope` function receiver." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SuspendFunctionOnCoroutineScope", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceWithIgnoreCaseEquals", + "shortDescription": { + "text": "Should be replaced with 'equals(..., ignoreCase = true)'" + }, + "fullDescription": { + "text": "Reports case-insensitive comparisons that can be replaced with 'equals(..., ignoreCase = true)'. By using 'equals()' you don't have to allocate extra strings with 'toLowerCase()' or 'toUpperCase()' to compare strings. The quick-fix replaces the case-insensitive comparison that uses 'toLowerCase()' or 'toUpperCase()' with 'equals(..., ignoreCase = true)'. Note: May change semantics for some locales. Example: 'fun main() {\n val a = \"KoTliN\"\n val b = \"KOTLIN\"\n println(a.toLowerCase() == b.toLowerCase())\n }' After the quick-fix is applied: 'fun main() {\n val a = \"KoTliN\"\n val b = \"KOTLIN\"\n println(a.equals(b, ignoreCase = true))\n }'", + "markdown": "Reports case-insensitive comparisons that can be replaced with `equals(..., ignoreCase = true)`.\n\nBy using `equals()` you don't have to allocate extra strings with `toLowerCase()` or `toUpperCase()` to compare strings.\n\nThe quick-fix replaces the case-insensitive comparison that uses `toLowerCase()` or `toUpperCase()` with `equals(..., ignoreCase = true)`.\n\n**Note:** May change semantics for some locales.\n\n**Example:**\n\n\n fun main() {\n val a = \"KoTliN\"\n val b = \"KOTLIN\"\n println(a.toLowerCase() == b.toLowerCase())\n }\n\nAfter the quick-fix is applied:\n\n\n fun main() {\n val a = \"KoTliN\"\n val b = \"KOTLIN\"\n println(a.equals(b, ignoreCase = true))\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceWithIgnoreCaseEquals", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DifferentMavenStdlibVersion", + "shortDescription": { + "text": "Library and maven plugin versions are different" + }, + "fullDescription": { + "text": "Reports different Kotlin stdlib and compiler versions. Using different versions of the Kotlin compiler and the standard library can lead to unpredictable runtime problems and should be avoided.", + "markdown": "Reports different Kotlin stdlib and compiler versions.\n\nUsing different versions of the Kotlin compiler and the standard library can lead to unpredictable\nruntime problems and should be avoided." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "DifferentMavenStdlibVersion", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin", + "index": 3, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceSubstringWithSubstringAfter", + "shortDescription": { + "text": "'substring' call should be replaced with 'substringAfter'" + }, + "fullDescription": { + "text": "Reports calls like 's.substring(s.indexOf(x))' that can be replaced with 's.substringAfter(x)'. Using 's.substringAfter(x)' makes your code simpler. The quick-fix replaces the 'substring' call with 'substringAfter'. Example: 'fun foo(s: String) {\n s.substring(s.indexOf('x'))\n }' After the quick-fix is applied: 'fun foo(s: String) {\n s.substringAfter('x')\n }'", + "markdown": "Reports calls like `s.substring(s.indexOf(x))` that can be replaced with `s.substringAfter(x)`.\n\nUsing `s.substringAfter(x)` makes your code simpler.\n\nThe quick-fix replaces the `substring` call with `substringAfter`.\n\n**Example:**\n\n\n fun foo(s: String) {\n s.substring(s.indexOf('x'))\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(s: String) {\n s.substringAfter('x')\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceSubstringWithSubstringAfter", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ImplicitNullableNothingType", + "shortDescription": { + "text": "Implicit 'Nothing?' type" + }, + "fullDescription": { + "text": "Reports variables and functions with the implicit Nothing? type. Example: 'fun foo() = null' The quick fix specifies the return type explicitly: 'fun foo(): Nothing? = null'", + "markdown": "Reports variables and functions with the implicit **Nothing?** type.\n\n**Example:**\n\n\n fun foo() = null\n\nThe quick fix specifies the return type explicitly:\n\n\n fun foo(): Nothing? = null\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "note", + "parameters": { + "suppressToolId": "ImplicitNullableNothingType", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantCompanionReference", + "shortDescription": { + "text": "Redundant 'Companion' reference" + }, + "fullDescription": { + "text": "Reports redundant 'Companion' reference. Example: 'class A {\n companion object {\n fun create() = A()\n }\n }\n fun test() {\n val s = A.Companion.create()\n }' After the quick-fix is applied: 'class A {\n companion object {\n fun create() = A()\n }\n }\n fun test() {\n val s = A.create()\n }'", + "markdown": "Reports redundant `Companion` reference.\n\n**Example:**\n\n\n class A {\n companion object {\n fun create() = A()\n }\n }\n fun test() {\n val s = A.Companion.create()\n }\n\nAfter the quick-fix is applied:\n\n\n class A {\n companion object {\n fun create() = A()\n }\n }\n fun test() {\n val s = A.create()\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantCompanionReference", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceAssociateFunction", + "shortDescription": { + "text": "'associate' can be replaced with 'associateBy' or 'associateWith'" + }, + "fullDescription": { + "text": "Reports calls to 'associate()' and 'associateTo()' that can be replaced with 'associateBy()' or 'associateWith()'. Both functions accept a transformer function applied to elements of a given sequence or collection (as a receiver). The pairs are then used to build the resulting 'Map'. Given the transformer refers to 'it', the 'associate[To]()' call can be replaced with more performant 'associateBy()' or 'associateWith()'. Examples: 'fun getKey(i: Int) = 1L\n fun getValue(i: Int) = 1L\n\n fun test() {\n arrayOf(1).associate { getKey(it) to it } // replaceable 'associate()'\n listOf(1).associate { it to getValue(it) } // replaceable 'associate()'\n }' After the quick-fix is applied: 'fun getKey(i: Int) = 1L\n fun getValue(i: Int) = 1L\n\n fun test() {\n arrayOf(1).associateBy { getKey(it) }\n listOf(1).associateWith { getValue(it) }\n }'", + "markdown": "Reports calls to `associate()` and `associateTo()` that can be replaced with `associateBy()` or `associateWith()`.\n\n\nBoth functions accept a transformer function applied to elements of a given sequence or collection (as a receiver).\nThe pairs are then used to build the resulting `Map`.\n\n\nGiven the transformer refers to `it`, the `associate[To]()` call can be replaced with more performant `associateBy()`\nor `associateWith()`.\n\n**Examples:**\n\n fun getKey(i: Int) = 1L\n fun getValue(i: Int) = 1L\n\n fun test() {\n arrayOf(1).associate { getKey(it) to it } // replaceable 'associate()'\n listOf(1).associate { it to getValue(it) } // replaceable 'associate()'\n }\n\nAfter the quick-fix is applied:\n\n fun getKey(i: Int) = 1L\n fun getValue(i: Int) = 1L\n\n fun test() {\n arrayOf(1).associateBy { getKey(it) }\n listOf(1).associateWith { getValue(it) }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceAssociateFunction", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KDocUnresolvedReference", + "shortDescription": { + "text": "Unresolved reference in KDoc" + }, + "fullDescription": { + "text": "Reports unresolved references in KDoc comments. Example: '/**\n * [unresolvedLink]\n */\n fun foo() {}' To fix the problem make the link valid.", + "markdown": "Reports unresolved references in KDoc comments.\n\n**Example:**\n\n\n /**\n * [unresolvedLink]\n */\n fun foo() {}\n\nTo fix the problem make the link valid." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "KDocUnresolvedReference", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ObsoleteExperimentalCoroutines", + "shortDescription": { + "text": "Experimental coroutines usages are deprecated since 1.3" + }, + "fullDescription": { + "text": "Reports code that uses experimental coroutines. Such usages are incompatible with Kotlin 1.3+ and should be updated.", + "markdown": "Reports code that uses experimental coroutines.\n\nSuch usages are incompatible with Kotlin 1.3+ and should be updated." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "ObsoleteExperimentalCoroutines", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Migration", + "index": 16, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LiftReturnOrAssignment", + "shortDescription": { + "text": "Return or assignment can be lifted out" + }, + "fullDescription": { + "text": "Reports 'if', 'when', and 'try' statements that can be converted to expressions by lifting the 'return' statement or an assignment out. Example: 'fun foo(arg: Int): String {\n when (arg) {\n 0 -> return \"Zero\"\n 1 -> return \"One\"\n else -> return \"Multiple\"\n }\n }' After the quick-fix is applied: 'fun foo(arg: Int): String {\n return when (arg) {\n 0 -> \"Zero\"\n 1 -> \"One\"\n else -> \"Multiple\"\n }\n }' If you would like this inspection to highlight more complex code with multi-statement branches, uncheck the option \"Report only if each branch is a single statement\".", + "markdown": "Reports `if`, `when`, and `try` statements that can be converted to expressions by lifting the `return` statement or an assignment out.\n\n**Example:**\n\n\n fun foo(arg: Int): String {\n when (arg) {\n 0 -> return \"Zero\"\n 1 -> return \"One\"\n else -> return \"Multiple\"\n }\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(arg: Int): String {\n return when (arg) {\n 0 -> \"Zero\"\n 1 -> \"One\"\n else -> \"Multiple\"\n }\n }\n\nIf you would like this inspection to highlight more complex code with multi-statement branches, uncheck the option \"Report only if each branch is a single statement\"." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "LiftReturnOrAssignment", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NestedLambdaShadowedImplicitParameter", + "shortDescription": { + "text": "Nested lambda has shadowed implicit parameter" + }, + "fullDescription": { + "text": "Reports nested lambdas with shadowed implicit parameters. Example: 'fun foo(listOfLists: List>) {\n listOfLists.forEach {\n it.forEach {\n println(it)\n }\n }\n}' After the quick-fix is applied: 'fun foo(listOfLists: List>) {\n listOfLists.forEach {\n it.forEach { it1 ->\n println(it1)\n }\n }\n}'", + "markdown": "Reports nested lambdas with shadowed implicit parameters.\n\n**Example:**\n\n\n fun foo(listOfLists: List>) {\n listOfLists.forEach {\n it.forEach {\n println(it)\n }\n }\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(listOfLists: List>) {\n listOfLists.forEach {\n it.forEach { it1 ->\n println(it1)\n }\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "NestedLambdaShadowedImplicitParameter", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SimplifyWhenWithBooleanConstantCondition", + "shortDescription": { + "text": "Simplifiable 'when'" + }, + "fullDescription": { + "text": "Reports 'when' expressions with the constant 'true' or 'false' branches. Simplify \"when\" quick-fix can be used to amend the code automatically. Examples: 'fun redundant() {\n when { // <== redundant, quick-fix simplifies the when expression to \"println(\"true\")\"\n true -> println(\"true\")\n else -> println(\"false\")\n }\n }'", + "markdown": "Reports `when` expressions with the constant `true` or `false` branches.\n\n**Simplify \"when\"** quick-fix can be used to amend the code automatically.\n\nExamples:\n\n\n fun redundant() {\n when { // <== redundant, quick-fix simplifies the when expression to \"println(\"true\")\"\n true -> println(\"true\")\n else -> println(\"false\")\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "SimplifyWhenWithBooleanConstantCondition", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantSamConstructor", + "shortDescription": { + "text": "Redundant SAM constructor" + }, + "fullDescription": { + "text": "Reports SAM (Single Abstract Method) constructor usages which can be replaced with lambdas. Example: 'fun main() {\n foo(Runnable { println(\"Hi!\") })\n }\n\n fun foo(other: Runnable) {}' After the quick-fix is applied: 'fun main() {\n foo( { println(\"Hi!\") })\n }\n\n fun foo(other: Runnable) {}'", + "markdown": "Reports SAM (Single Abstract Method) constructor usages which can be replaced with lambdas.\n\n**Example:**\n\n\n fun main() {\n foo(Runnable { println(\"Hi!\") })\n }\n\n fun foo(other: Runnable) {}\n\nAfter the quick-fix is applied:\n\n\n fun main() {\n foo( { println(\"Hi!\") })\n }\n\n fun foo(other: Runnable) {}\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantSamConstructor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KotlinConstantConditions", + "shortDescription": { + "text": "Constant conditions" + }, + "fullDescription": { + "text": "Reports non-trivial conditions and values that are statically known to be always true, false, null or zero. While sometimes intended, often this is a sign of logical error in the program. Additionally, reports never reachable 'when' branches and some expressions that are statically known to fail always. Examples: 'fun process(x: Int?) {\n val isNull = x == null\n if (!isNull) {\n if (x != null) {} // condition is always true\n require(x!! < 0 && x > 10) // condition is always false\n } else {\n println(x!!) // !! operator will always fail\n }\n}\nfun process(v: Any) {\n when(v) {\n is CharSequence -> println(v as Int) // cast will always fail\n is String -> println(v) // branch is unreachable\n }\n}' Uncheck the \"Warn when constant is stored in variable\" option to avoid reporting of variables having constant values not in conditions. New in 2021.3", + "markdown": "Reports non-trivial conditions and values that are statically known to be always true, false, null or zero. While sometimes intended, often this is a sign of logical error in the program. Additionally, reports never reachable `when` branches and some expressions that are statically known to fail always.\n\nExamples:\n\n\n fun process(x: Int?) {\n val isNull = x == null\n if (!isNull) {\n if (x != null) {} // condition is always true\n require(x!! < 0 && x > 10) // condition is always false\n } else {\n println(x!!) // !! operator will always fail\n }\n }\n fun process(v: Any) {\n when(v) {\n is CharSequence -> println(v as Int) // cast will always fail\n is String -> println(v) // branch is unreachable\n }\n }\n\n\nUncheck the \"Warn when constant is stored in variable\" option to avoid reporting of variables having constant values not in conditions.\n\nNew in 2021.3" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "KotlinConstantConditions", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InconsistentCommentForJavaParameter", + "shortDescription": { + "text": "Inconsistent comment for Java parameter" + }, + "fullDescription": { + "text": "Reports inconsistent parameter names for Java method calls specified in a comment block. Examples: '// Java\n public class JavaService {\n public void invoke(String command) {}\n }' '// Kotlin\n fun main() {\n JavaService().invoke(/* name = */ \"fix\")\n }' The quick fix corrects the parameter name in the comment block: 'fun main() {\n JavaService().invoke(/* command = */ \"fix\")\n }'", + "markdown": "Reports inconsistent parameter names for **Java** method calls specified in a comment block.\n\n**Examples:**\n\n\n // Java\n public class JavaService {\n public void invoke(String command) {}\n }\n\n\n // Kotlin\n fun main() {\n JavaService().invoke(/* name = */ \"fix\")\n }\n\nThe quick fix corrects the parameter name in the comment block:\n\n\n fun main() {\n JavaService().invoke(/* command = */ \"fix\")\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "InconsistentCommentForJavaParameter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Naming conventions", + "index": 49, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ForEachParameterNotUsed", + "shortDescription": { + "text": "Iterated elements are not used in forEach" + }, + "fullDescription": { + "text": "Reports 'forEach' loops that do not use iterable values. Example: 'listOf(1, 2, 3).forEach { }' The quick fix introduces anonymous parameter in the 'forEach' section: 'listOf(1, 2, 3).forEach { _ -> }'", + "markdown": "Reports `forEach` loops that do not use iterable values.\n\n**Example:**\n\n\n listOf(1, 2, 3).forEach { }\n\nThe quick fix introduces anonymous parameter in the `forEach` section:\n\n\n listOf(1, 2, 3).forEach { _ -> }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "note", + "parameters": { + "suppressToolId": "ForEachParameterNotUsed", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LeakingThis", + "shortDescription": { + "text": "Leaking 'this' in constructor" + }, + "fullDescription": { + "text": "Reports unsafe operations with 'this' during object construction including: Accessing a non-final property during class initialization: from a constructor or property initialization Calling a non-final function during class initialization Using 'this' as a function argument in a constructor of a non-final class If other classes inherit from the given class, they may not be fully initialized at the moment when an unsafe operation is carried out. Example: 'abstract class Base {\n val code = calculate()\n abstract fun calculate(): Int\n }\n\n class Derived(private val x: Int) : Base() {\n override fun calculate() = x\n }\n\n fun testIt() {\n println(Derived(42).code) // Expected: 42, actual: 0\n }'", + "markdown": "Reports unsafe operations with `this` during object construction including:\n\n* Accessing a non-final property during class initialization: from a constructor or property initialization\n* Calling a non-final function during class initialization\n* Using `this` as a function argument in a constructor of a non-final class\n\n\nIf other classes inherit from the given class,\nthey may not be fully initialized at the moment when an unsafe operation is carried out.\n\n**Example:**\n\n\n abstract class Base {\n val code = calculate()\n abstract fun calculate(): Int\n }\n\n class Derived(private val x: Int) : Base() {\n override fun calculate() = x\n }\n\n fun testIt() {\n println(Derived(42).code) // Expected: 42, actual: 0\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "LeakingThis", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RemoveRedundantCallsOfConversionMethods", + "shortDescription": { + "text": "Redundant call of conversion method" + }, + "fullDescription": { + "text": "Reports redundant calls to conversion methods (for example, 'toString()' on a 'String' or 'toDouble()' on a 'Double'). Use the 'Remove redundant calls of the conversion method' quick-fix to clean up the code.", + "markdown": "Reports redundant calls to conversion methods (for example, `toString()` on a `String` or `toDouble()` on a `Double`).\n\nUse the 'Remove redundant calls of the conversion method' quick-fix to clean up the code." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RemoveRedundantCallsOfConversionMethods", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AddVarianceModifier", + "shortDescription": { + "text": "Type parameter can have 'in' or 'out' variance" + }, + "fullDescription": { + "text": "Reports type parameters that can have 'in' or 'out' variance. Using 'in' and 'out' variance provides more precise type inference in Kotlin and clearer code semantics. Example: 'class Box(val obj: T)\n\n fun consumeString(box: Box) {}\n fun consumeCharSequence(box: Box) {}\n\n fun usage(box: Box) {\n consumeString(box)\n consumeCharSequence(box) // Compilation error\n }' The quick-fix adds the matching variance modifier: 'class Box(val obj: T)\n\n fun consumeString(box: Box) {}\n fun consumeCharSequence(box: Box) {}\n\n fun usage(box: Box) ++{\n consumeString(box)\n consumeCharSequence(box) // OK\n }'", + "markdown": "Reports type parameters that can have `in` or `out` variance.\n\nUsing `in` and `out` variance provides more precise type inference in Kotlin and clearer code semantics.\n\n**Example:**\n\n\n class Box(val obj: T)\n\n fun consumeString(box: Box) {}\n fun consumeCharSequence(box: Box) {}\n\n fun usage(box: Box) {\n consumeString(box)\n consumeCharSequence(box) // Compilation error\n }\n\nThe quick-fix adds the matching variance modifier:\n\n\n class Box(val obj: T)\n\n fun consumeString(box: Box) {}\n fun consumeCharSequence(box: Box) {}\n\n fun usage(box: Box) ++{\n consumeString(box)\n consumeCharSequence(box) // OK\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "AddVarianceModifier", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KotlinThrowableNotThrown", + "shortDescription": { + "text": "Throwable not thrown" + }, + "fullDescription": { + "text": "Reports instantiations of 'Throwable' or its subclasses, when the created 'Throwable' is never actually thrown. The reported code indicates mistakes that are hard to catch in tests. Also, this inspection reports method calls that return instances of 'Throwable' or its subclasses, when the resulting 'Throwable' instance is not thrown. Example: 'fun check(condition: Boolean) {\n if (!condition) /* throw is missing here */ IllegalArgumentException(\"condition is not met\");\n }\n\n fun createError() = RuntimeException()\n\n fun foo() {\n /* throw is missing here */ createError()\n }'", + "markdown": "Reports instantiations of `Throwable` or its subclasses, when the created `Throwable` is never actually thrown.\n\nThe reported code indicates mistakes that are hard to catch in tests.\n\n\nAlso, this inspection reports method calls that return instances of `Throwable` or its subclasses,\nwhen the resulting `Throwable` instance is not thrown.\n\n**Example:**\n\n\n fun check(condition: Boolean) {\n if (!condition) /* throw is missing here */ IllegalArgumentException(\"condition is not met\");\n }\n\n fun createError() = RuntimeException()\n\n fun foo() {\n /* throw is missing here */ createError()\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ThrowableNotThrown", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KotlinSealedInheritorsInJava", + "shortDescription": { + "text": "Inheritance of Kotlin sealed interface/class from Java" + }, + "fullDescription": { + "text": "Reports attempts to inherit from Kotlin sealed interfaces or classes in Java code. Example: '// Kotlin file: MathExpression.kt\n\nsealed class MathExpression\n\ndata class Const(val number: Double) : MathExpression()\ndata class Sum(val e1: MathExpression, val e2: MathExpression) : MathExpression()' '// Java file: NotANumber.java\n\npublic class NotANumber extends MathExpression {\n}'", + "markdown": "Reports attempts to inherit from Kotlin sealed interfaces or classes in Java code.\n\n**Example:**\n\n\n // Kotlin file: MathExpression.kt\n\n sealed class MathExpression\n\n data class Const(val number: Double) : MathExpression()\n data class Sum(val e1: MathExpression, val e2: MathExpression) : MathExpression()\n\n\n // Java file: NotANumber.java\n\n public class NotANumber extends MathExpression {\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "error", + "parameters": { + "suppressToolId": "KotlinSealedInheritorsInJava", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Java interop issues", + "index": 70, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SimplifyNegatedBinaryExpression", + "shortDescription": { + "text": "Negated boolean expression can be simplified" + }, + "fullDescription": { + "text": "Reports negated boolean expressions that can be simplified. The quick-fix simplifies the boolean expression. Example: 'fun test(n: Int) {\n !(0 == 1)\n }' After the quick-fix is applied: 'fun test(n: Int) {\n 0 != 1\n }' Please note that this action may change code semantics if IEEE-754 NaN values are involved: 'fun main() {\n println(!(Double.NaN >= 0)) // true\n }' After the quick-fix is applied: 'fun main() {\n println(Double.NaN < 0) // false\n }'", + "markdown": "Reports negated boolean expressions that can be simplified.\n\nThe quick-fix simplifies the boolean expression.\n\n**Example:**\n\n\n fun test(n: Int) {\n !(0 == 1)\n }\n\nAfter the quick-fix is applied:\n\n\n fun test(n: Int) {\n 0 != 1\n }\n\nPlease note that this action may change code semantics if IEEE-754 NaN values are involved:\n\n\n fun main() {\n println(!(Double.NaN >= 0)) // true\n }\n\nAfter the quick-fix is applied:\n\n\n fun main() {\n println(Double.NaN < 0) // false\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "SimplifyNegatedBinaryExpression", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MemberVisibilityCanBePrivate", + "shortDescription": { + "text": "Class member can have 'private' visibility" + }, + "fullDescription": { + "text": "Reports declarations that can be made 'private' to follow the encapsulation principle. Example: 'class Service(val url: String) {\n fun connect(): URLConnection = URL(url).openConnection()\n}' After the quick-fix is applied (considering there are no usages of 'url' outside of 'Service' class): 'class Service(private val url: String) {\n fun connect(): URLConnection = URL(url).openConnection()\n}'", + "markdown": "Reports declarations that can be made `private` to follow the encapsulation principle.\n\n**Example:**\n\n\n class Service(val url: String) {\n fun connect(): URLConnection = URL(url).openConnection()\n }\n\nAfter the quick-fix is applied (considering there are no usages of `url` outside of `Service` class):\n\n\n class Service(private val url: String) {\n fun connect(): URLConnection = URL(url).openConnection()\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "MemberVisibilityCanBePrivate", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RemoveForLoopIndices", + "shortDescription": { + "text": "Unused loop index" + }, + "fullDescription": { + "text": "Reports 'for' loops iterating over a collection using the 'withIndex()' function and not using the index variable. Use the \"Remove indices in 'for' loop\" quick-fix to clean up the code. Examples: 'fun foo(bar: List) {\n for ((index : Int, value: String) in bar.withIndex()) { // <== 'index' is unused\n println(value)\n }\n }' After the quick-fix is applied: 'fun foo(bar: List) {\n for (value: String in bar) { // <== '.withIndex()' and 'index' are removed\n println(value)\n }\n }'", + "markdown": "Reports `for` loops iterating over a collection using the `withIndex()` function and not using the index variable.\n\nUse the \"Remove indices in 'for' loop\" quick-fix to clean up the code.\n\n**Examples:**\n\n\n fun foo(bar: List) {\n for ((index : Int, value: String) in bar.withIndex()) { // <== 'index' is unused\n println(value)\n }\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(bar: List) {\n for (value: String in bar) { // <== '.withIndex()' and 'index' are removed\n println(value)\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RemoveForLoopIndices", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantSuspendModifier", + "shortDescription": { + "text": "Redundant 'suspend' modifier" + }, + "fullDescription": { + "text": "Reports 'suspend' modifier as redundant if no other suspending functions are called inside.", + "markdown": "Reports `suspend` modifier as redundant if no other suspending functions are called inside." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantSuspendModifier", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspiciousAsDynamic", + "shortDescription": { + "text": "Suspicious 'asDynamic' member invocation" + }, + "fullDescription": { + "text": "Reports usages of 'asDynamic' function on a receiver of dynamic type. 'asDynamic' function has no effect for expressions of dynamic type. 'asDynamic' function on a receiver of dynamic type can lead to runtime problems because 'asDynamic' will be executed in JavaScript environment, and such function may not be present at runtime. The intended way is to use this function on usual Kotlin type. Remove \"asDynamic\" invocation quick-fix can be used to amend the code automatically. Example: 'fun wrongUsage(d: Dynamic) {\n d.asDynamic().foo() // <== redundant, quick-fix simplifies the call expression to \"d.foo()\"\n }'", + "markdown": "Reports usages of `asDynamic` function on a receiver of dynamic type.\n\n`asDynamic` function has no effect for expressions of dynamic type.\n\n`asDynamic` function on a receiver of dynamic type can lead to runtime problems because `asDynamic`\nwill be executed in JavaScript environment, and such function may not be present at runtime.\nThe intended way is to use this function on usual Kotlin type.\n\n**Remove \"asDynamic\" invocation** quick-fix can be used to amend the code automatically.\n\nExample:\n\n\n fun wrongUsage(d: Dynamic) {\n d.asDynamic().foo() // <== redundant, quick-fix simplifies the call expression to \"d.foo()\"\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "SuspiciousAsDynamic", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FilterIsInstanceCallWithClassLiteralArgument", + "shortDescription": { + "text": "'filterIsInstance' call with a class literal argument" + }, + "fullDescription": { + "text": "Reports calls of the Kotlin standard library function 'filterIsInstance' with a class literal argument. It is more idiomatic to use a version of this function with a reified type parameter, to avoid the '::class.java' syntax. Note: inspection is not reported for generic class literals because the 'Class<*, *>' syntax in the type argument list may be undesirable. Example: 'fun foo(list: List<*>) {\n list.filterIsInstance(Int::class.java)\n }' After the quick-fix is applied: 'fun foo(list: List<*>) {\n list.filterIsInstance()\n }'", + "markdown": "Reports calls of the Kotlin standard library function `filterIsInstance` with a class literal argument. It is more idiomatic to use a version of this function with a reified type parameter, to avoid the `::class.java` syntax.\n\nNote: inspection is not reported for generic class literals because the `Class<*, *>` syntax in the type argument list may be undesirable.\n\nExample:\n\n\n fun foo(list: List<*>) {\n list.filterIsInstance(Int::class.java)\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(list: List<*>) {\n list.filterIsInstance()\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "FilterIsInstanceCallWithClassLiteralArgument", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FromClosedRangeMigration", + "shortDescription": { + "text": "MIN_VALUE step in fromClosedRange() since 1.3" + }, + "fullDescription": { + "text": "Reports 'IntProgression.fromClosedRange()' and 'LongProgression.fromClosedRange()' with 'MIN_VALUE' step. It is prohibited to call 'IntProgression.fromClosedRange()' and 'LongProgression.fromClosedRange()' with 'MIN_VALUE' step. All such calls should be checked during migration to Kotlin 1.3+. Example: 'IntProgression.fromClosedRange(12, 143, Int.MIN_VALUE)' To fix the problem change the step of the progression.", + "markdown": "Reports `IntProgression.fromClosedRange()` and `LongProgression.fromClosedRange()` with `MIN_VALUE` step.\n\n\nIt is prohibited to call `IntProgression.fromClosedRange()` and `LongProgression.fromClosedRange()` with\n`MIN_VALUE` step. All such calls should be checked during migration to Kotlin 1.3+.\n\n**Example:**\n\n\n IntProgression.fromClosedRange(12, 143, Int.MIN_VALUE)\n\nTo fix the problem change the step of the progression." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "FromClosedRangeMigration", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Migration", + "index": 16, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RemoveRedundantBackticks", + "shortDescription": { + "text": "Redundant backticks" + }, + "fullDescription": { + "text": "Reports redundant backticks in references. Some of the Kotlin keywords are valid identifiers in Java, for example: 'in', 'object', 'is'. If a Java library uses a Kotlin keyword for a method, you can still call the method escaping it with the backtick character ('`'), for example, 'foo.`is`(bar)'. Sometimes this escaping is redundant and can be safely omitted. The inspection discovers and reports such cases and is paired with the 'Remove redundant backticks' quick-fix, which allows you to amend the highlighted code. Examples: 'fun `is`(x: String) {}\n fun foo() {\n `is`(\"bar\") // 'is' is a keyword, backticks are required\n }\n\n fun `test that smth works as designed`() {} // OK, complex identifier for readability improvement\n\n val `a` = 1 // no need for backticks'", + "markdown": "Reports redundant backticks in references.\n\n\nSome of the Kotlin keywords are valid identifiers in Java, for example: `in`, `object`, `is`.\nIf a Java library uses a Kotlin keyword for a method, you can still call the method escaping it\nwith the backtick character (`````), for example, ``foo.`is`(bar)``.\nSometimes this escaping is redundant and can be safely omitted. The inspection discovers and reports such cases and is\npaired with the 'Remove redundant backticks' quick-fix, which allows you to amend the highlighted code.\n\n**Examples:**\n\n\n fun `is`(x: String) {}\n fun foo() {\n `is`(\"bar\") // 'is' is a keyword, backticks are required\n }\n\n fun `test that smth works as designed`() {} // OK, complex identifier for readability improvement\n\n val `a` = 1 // no need for backticks\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RemoveRedundantBackticks", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceReadLineWithReadln", + "shortDescription": { + "text": "'readLine' can be replaced with 'readln' or 'readlnOrNull'" + }, + "fullDescription": { + "text": "Reports calls to 'readLine()' that can be replaced with 'readln()' or 'readlnOrNull()'. Using corresponding functions makes your code simpler. The quick-fix replaces 'readLine()!!' with 'readln()' and 'readLine()' with 'readlnOrNull()'. Examples: 'val x = readLine()!!\n val y = readLine()?.length' After the quick-fix is applied: 'val x = readln()\n val y = readlnOrNull()?.length'", + "markdown": "Reports calls to `readLine()` that can be replaced with `readln()` or `readlnOrNull()`.\n\n\nUsing corresponding functions makes your code simpler.\n\n\nThe quick-fix replaces `readLine()!!` with `readln()` and `readLine()` with `readlnOrNull()`.\n\n**Examples:**\n\n\n val x = readLine()!!\n val y = readLine()?.length\n\nAfter the quick-fix is applied:\n\n\n val x = readln()\n val y = readlnOrNull()?.length\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceReadLineWithReadln", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SelfAssignment", + "shortDescription": { + "text": "Redundant assignment" + }, + "fullDescription": { + "text": "Reports assignments of a variable to itself. The quick-fix removes the redundant assignment. Example: 'fun test() {\n var bar = 1\n bar = bar\n }' After the quick-fix is applied: 'fun test() {\n var bar = 1\n }'", + "markdown": "Reports assignments of a variable to itself.\n\nThe quick-fix removes the redundant assignment.\n\n**Example:**\n\n\n fun test() {\n var bar = 1\n bar = bar\n }\n\nAfter the quick-fix is applied:\n\n\n fun test() {\n var bar = 1\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SelfAssignment", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RecursiveEqualsCall", + "shortDescription": { + "text": "Recursive equals call" + }, + "fullDescription": { + "text": "Reports recursive 'equals'('==') calls. In Kotlin, '==' compares object values by calling 'equals' method under the hood. '===', on the other hand, compares objects by reference. '===' is commonly used in 'equals' method implementation. But '===' may be mistakenly mixed up with '==' leading to infinite recursion. Example: 'class X {\n override fun equals(other: Any?): Boolean {\n if (this == other) return true\n return false\n }\n }' After the quick-fix is applied: 'class X {\n override fun equals(other: Any?): Boolean {\n if (this === other) return true\n return false\n }\n }'", + "markdown": "Reports recursive `equals`(`==`) calls.\n\n\nIn Kotlin, `==` compares object values by calling `equals` method under the hood.\n`===`, on the other hand, compares objects by reference.\n\n\n`===` is commonly used in `equals` method implementation.\nBut `===` may be mistakenly mixed up with `==` leading to infinite recursion.\n\n**Example:**\n\n\n class X {\n override fun equals(other: Any?): Boolean {\n if (this == other) return true\n return false\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class X {\n override fun equals(other: Any?): Boolean {\n if (this === other) return true\n return false\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "RecursiveEqualsCall", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SimplifyNestedEachInScopeFunction", + "shortDescription": { + "text": "Scope function with nested forEach can be simplified" + }, + "fullDescription": { + "text": "Reports 'forEach' functions in the scope functions such as 'also' or 'apply' that can be simplified. Convert forEach call to onEach quick-fix can be used to amend the code automatically. Examples: 'fun test(list: List) {\n val x = list.also { it.forEach { it + 4 } }.toString()\n val y = list.apply { forEach { println(it) } }\n }' After the quick-fix is applied: 'fun test(list: List) {\n val x = list.onEach { it + 4 }.toString()\n val y = list.onEach { println(it) }\n }'", + "markdown": "Reports `forEach` functions in the scope functions such as `also` or `apply` that can be simplified.\n\n**Convert forEach call to onEach** quick-fix can be used to amend the code automatically.\n\nExamples:\n\n\n fun test(list: List) {\n val x = list.also { it.forEach { it + 4 } }.toString()\n val y = list.apply { forEach { println(it) } }\n }\n\nAfter the quick-fix is applied:\n\n\n fun test(list: List) {\n val x = list.onEach { it + 4 }.toString()\n val y = list.onEach { println(it) }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "SimplifyNestedEachInScopeFunction", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnsafeCastFromDynamic", + "shortDescription": { + "text": "Implicit (unsafe) cast from dynamic type" + }, + "fullDescription": { + "text": "Reports expressions with a dynamic type in the specified inspection scope that are implicitly cast to another type.", + "markdown": "Reports expressions with a dynamic type in the specified inspection scope that are implicitly cast to another type." + }, + "defaultConfiguration": { + "enabled": true, + "level": "note", + "parameters": { + "suppressToolId": "UnsafeCastFromDynamic", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PublicApiImplicitType", + "shortDescription": { + "text": "Public API declaration with implicit return type" + }, + "fullDescription": { + "text": "Reports 'public' and 'protected' functions and properties that have an implicit return type. For API stability reasons, it's recommended to specify such types explicitly. Example: 'fun publicFunctionWhichAbusesTypeInference() =\n otherFunctionWithNotObviousReturnType() ?: yetAnotherFunctionWithNotObviousReturnType()' After the quick-fix is applied: 'fun publicFunctionWhichAbusesTypeInference(): Api =\n otherFunctionWithNotObviousReturnType() ?: yetAnotherFunctionWithNotObviousReturnType()'", + "markdown": "Reports `public` and `protected` functions and properties that have an implicit return type.\nFor API stability reasons, it's recommended to specify such types explicitly.\n\n**Example:**\n\n\n fun publicFunctionWhichAbusesTypeInference() =\n otherFunctionWithNotObviousReturnType() ?: yetAnotherFunctionWithNotObviousReturnType()\n\nAfter the quick-fix is applied:\n\n\n fun publicFunctionWhichAbusesTypeInference(): Api =\n otherFunctionWithNotObviousReturnType() ?: yetAnotherFunctionWithNotObviousReturnType()\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "PublicApiImplicitType", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Other problems", + "index": 54, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DeclaringClassMigration", + "shortDescription": { + "text": "Deprecated 'Enum.declaringClass' property" + }, + "fullDescription": { + "text": "Reports 'declaringClass' property calls on Enum that will lead to compilation error since 1.9. 'Enum.getDeclaringClass' is among \"hidden\" Java functions which aren't normally visible by resolve. However, it's visible via synthetic property that is a front-end bug. More details: KT-49653 Deprecate and remove Enum.declaringClass synthetic property The quick-fix replaces a call with 'declaringJavaClass'. Example: 'fun > foo(values: Array) {\n EnumSet.noneOf(values.first().declaringClass)\n }' After the quick-fix is applied: 'fun > foo(values: Array) {\n EnumSet.noneOf(values.first().declaringJavaClass)\n }' This inspection only reports if the Kotlin language level of the project or module is 1.7 or higher.", + "markdown": "Reports 'declaringClass' property calls on Enum that will lead to compilation error since 1.9.\n\n'Enum.getDeclaringClass' is among \"hidden\" Java functions which aren't normally visible by resolve. However, it's visible via synthetic\nproperty that is a front-end bug.\n\n**More details:** [KT-49653 Deprecate and remove Enum.declaringClass synthetic\nproperty](https://youtrack.jetbrains.com/issue/KT-49653)\n\nThe quick-fix replaces a call with 'declaringJavaClass'.\n\n**Example:**\n\n\n fun > foo(values: Array) {\n EnumSet.noneOf(values.first().declaringClass)\n }\n\nAfter the quick-fix is applied:\n\n\n fun > foo(values: Array) {\n EnumSet.noneOf(values.first().declaringJavaClass)\n }\n\nThis inspection only reports if the Kotlin language level of the project or module is 1.7 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "DeclaringClassMigration", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Migration", + "index": 16, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceMapIndexedWithListGenerator", + "shortDescription": { + "text": "Replace 'mapIndexed' with List generator" + }, + "fullDescription": { + "text": "Reports a 'mapIndexed' call that can be replaced by 'List' generator. Example: 'val a = listOf(1, 2, 3).mapIndexed { i, _ ->\n i + 42\n }' After the quick-fix is applied: 'val a = List(listOf(1, 2, 3).size) { i ->\n i + 42\n }'", + "markdown": "Reports a `mapIndexed` call that can be replaced by `List` generator.\n\n**Example:**\n\n\n val a = listOf(1, 2, 3).mapIndexed { i, _ ->\n i + 42\n }\n\nAfter the quick-fix is applied:\n\n\n val a = List(listOf(1, 2, 3).size) { i ->\n i + 42\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceMapIndexedWithListGenerator", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ControlFlowWithEmptyBody", + "shortDescription": { + "text": "Control flow with empty body" + }, + "fullDescription": { + "text": "Reports 'if', 'while', 'do' or 'for' statements with empty bodies. While occasionally intended, this construction is confusing and often the result of a typo. The quick-fix removes a statement. Example: 'if (a > b) {}'", + "markdown": "Reports `if`, `while`, `do` or `for` statements with empty bodies.\n\nWhile occasionally intended, this construction is confusing and often the result of a typo.\n\nThe quick-fix removes a statement.\n\n**Example:**\n\n\n if (a > b) {}\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ControlFlowWithEmptyBody", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ExplicitThis", + "shortDescription": { + "text": "Redundant explicit 'this'" + }, + "fullDescription": { + "text": "Reports an explicit 'this' when it can be omitted. Example: 'class C {\n private val i = 1\n fun f() = this.i\n }' The quick-fix removes the redundant 'this': 'class C {\n private val i = 1\n fun f() = i\n }'", + "markdown": "Reports an explicit `this` when it can be omitted.\n\n**Example:**\n\n\n class C {\n private val i = 1\n fun f() = this.i\n }\n\nThe quick-fix removes the redundant `this`:\n\n\n class C {\n private val i = 1\n fun f() = i\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ExplicitThis", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NullChecksToSafeCall", + "shortDescription": { + "text": "Null-checks can be replaced with safe-calls" + }, + "fullDescription": { + "text": "Reports chained null-checks that can be replaced with safe-calls. Example: 'fun test(my: My?) {\n if (my != null && my.foo() != null) {}\n }' After the quick-fix is applied: 'fun test(my: My?) {\n if (my?.foo() != null) {}\n }'", + "markdown": "Reports chained null-checks that can be replaced with safe-calls.\n\n**Example:**\n\n\n fun test(my: My?) {\n if (my != null && my.foo() != null) {}\n }\n\nAfter the quick-fix is applied:\n\n\n fun test(my: My?) {\n if (my?.foo() != null) {}\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "NullChecksToSafeCall", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LoopToCallChain", + "shortDescription": { + "text": "Loop can be replaced with stdlib operations" + }, + "fullDescription": { + "text": "Reports 'for' loops that can be replaced with a sequence of stdlib operations (like 'map', 'filter', and so on). Example: 'fun foo(list: List): List {\n val result = ArrayList()\n for (s in list) {\n if (s.length > 0)\n result.add(s.hashCode())\n }\n return result\n}' After the quick-fix is applied: 'fun foo(list: List): List {\n val result = list\n .filter { it.length > 0 }\n .map { it.hashCode() }\n return result\n}'", + "markdown": "Reports `for` loops that can be replaced with a sequence of stdlib operations (like `map`, `filter`, and so on).\n\n**Example:**\n\n\n fun foo(list: List): List {\n val result = ArrayList()\n for (s in list) {\n if (s.length > 0)\n result.add(s.hashCode())\n }\n return result\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(list: List): List {\n val result = list\n .filter { it.length > 0 }\n .map { it.hashCode() }\n return result\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "LoopToCallChain", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RemoveEmptyClassBody", + "shortDescription": { + "text": "Replace empty class body" + }, + "fullDescription": { + "text": "Reports declarations of classes and objects with an empty body. Use the 'Remove redundant empty class body' quick-fix to clean up the code. Examples: 'class EmptyA() {} // <== empty body\n\n class EmptyB {\n companion object {} // <== empty body\n }\n\n fun emptyC() {\n object {} // <== anonymous object, it's ok (not reported)\n }' After the quick fix is applied: 'class EmptyA()\n\n class EmptyB {\n companion object\n }\n\n fun emptyC() {\n object {}\n }'", + "markdown": "Reports declarations of classes and objects with an empty body.\n\nUse the 'Remove redundant empty class body' quick-fix to clean up the code.\n\n**Examples:**\n\n\n class EmptyA() {} // <== empty body\n\n class EmptyB {\n companion object {} // <== empty body\n }\n\n fun emptyC() {\n object {} // <== anonymous object, it's ok (not reported)\n }\n\nAfter the quick fix is applied:\n\n\n class EmptyA()\n\n class EmptyB {\n companion object\n }\n\n fun emptyC() {\n object {}\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RemoveEmptyClassBody", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CanBeParameter", + "shortDescription": { + "text": "Constructor parameter is never used as a property" + }, + "fullDescription": { + "text": "Reports primary constructor parameters that can have 'val' or 'var' removed. Class properties declared in the constructor increase memory consumption. If the parameter value is only used in the constructor, you can omit them. Note that the referenced object might be garbage-collected earlier. Example: 'class Task(val name: String) {\n init {\n print(\"Task created: $name\")\n }\n }' The quick-fix removes the extra 'val' or 'var' keyword: 'class Task(name: String) {\n init {\n print(\"Task created: $name\")\n }\n }'", + "markdown": "Reports primary constructor parameters that can have `val` or `var` removed.\n\n\nClass properties declared in the constructor increase memory consumption.\nIf the parameter value is only used in the constructor, you can omit them.\n\nNote that the referenced object might be garbage-collected earlier.\n\n**Example:**\n\n\n class Task(val name: String) {\n init {\n print(\"Task created: $name\")\n }\n }\n\nThe quick-fix removes the extra `val` or `var` keyword:\n\n\n class Task(name: String) {\n init {\n print(\"Task created: $name\")\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CanBeParameter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantReturnLabel", + "shortDescription": { + "text": "Redundant 'return' label" + }, + "fullDescription": { + "text": "Reports redundant return labels outside of lambda expressions. Example: 'fun test() {\n return@test\n }' After the quick-fix is applied: 'fun test() {\n return\n }'", + "markdown": "Reports redundant return labels outside of lambda expressions.\n\n**Example:**\n\n\n fun test() {\n return@test\n }\n\nAfter the quick-fix is applied:\n\n\n fun test() {\n return\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RedundantReturnLabel", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnusedMainParameter", + "shortDescription": { + "text": "Main parameter is not necessary" + }, + "fullDescription": { + "text": "Reports 'main' function with an unused single parameter.", + "markdown": "Reports `main` function with an unused single parameter." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "UnusedMainParameter", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FunctionWithLambdaExpressionBody", + "shortDescription": { + "text": "Function with '= { ... }' and inferred return type" + }, + "fullDescription": { + "text": "Reports functions with '= { ... }' and inferred return type. Example: 'fun sum(a: Int, b: Int) = { a + b } // The return type of this function is '() -> Int'.' The quick fix removes braces: 'fun sum(a: Int, b: Int) = a + b'", + "markdown": "Reports functions with `= { ... }` and inferred return type.\n\n**Example:**\n\n\n fun sum(a: Int, b: Int) = { a + b } // The return type of this function is '() -> Int'.\n\nThe quick fix removes braces:\n\n\n fun sum(a: Int, b: Int) = a + b\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "FunctionWithLambdaExpressionBody", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PackageName", + "shortDescription": { + "text": "Package naming convention" + }, + "fullDescription": { + "text": "Reports package names that do not follow the naming conventions. You can specify the required pattern in the inspection options. Recommended naming conventions: names of packages are always lowercase and should not contain underscores. Example: 'org.example.project' Using multi-word names is generally discouraged, but if you do need to use multiple words, you can either just concatenate them together or use camel case Example: 'org.example.myProject'", + "markdown": "Reports package names that do not follow the naming conventions.\n\nYou can specify the required pattern in the inspection options.\n\n[Recommended naming conventions](https://kotlinlang.org/docs/coding-conventions.html#naming-rules): names of packages are always lowercase and should not contain underscores.\n\n**Example:**\n`org.example.project`\n\nUsing multi-word names is generally discouraged, but if you do need to use multiple words, you can either just concatenate them together or use camel case\n\n**Example:**\n`org.example.myProject`" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "PackageName", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Naming conventions", + "index": 49, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ArrayInDataClass", + "shortDescription": { + "text": "Array property in data class" + }, + "fullDescription": { + "text": "Reports properties with an 'Array' type in a 'data' class without overridden 'equals()' or 'hashCode()'. Array parameters are compared by reference equality, which is likely an unexpected behavior. It is strongly recommended to override 'equals()' and 'hashCode()' in such cases. Example: 'data class Text(val lines: Array)' The quick-fix generates missing 'equals()' and 'hashCode()' implementations: 'data class Text(val lines: Array) {\n override fun equals(other: Any?): Boolean {\n if (this === other) return true\n if (javaClass != other?.javaClass) return false\n\n other as Text\n\n if (!lines.contentEquals(other.lines)) return false\n\n return true\n }\n\n override fun hashCode(): Int {\n return lines.contentHashCode()\n }\n }'", + "markdown": "Reports properties with an `Array` type in a `data` class without overridden `equals()` or `hashCode()`.\n\n\nArray parameters are compared by reference equality, which is likely an unexpected behavior.\nIt is strongly recommended to override `equals()` and `hashCode()` in such cases.\n\n**Example:**\n\n\n data class Text(val lines: Array)\n\nThe quick-fix generates missing `equals()` and `hashCode()` implementations:\n\n\n data class Text(val lines: Array) {\n override fun equals(other: Any?): Boolean {\n if (this === other) return true\n if (javaClass != other?.javaClass) return false\n\n other as Text\n\n if (!lines.contentEquals(other.lines)) return false\n\n return true\n }\n\n override fun hashCode(): Int {\n return lines.contentHashCode()\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "note", + "parameters": { + "suppressToolId": "ArrayInDataClass", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConvertTwoComparisonsToRangeCheck", + "shortDescription": { + "text": "Two comparisons should be converted to a range check" + }, + "fullDescription": { + "text": "Reports two consecutive comparisons that can be converted to a range check. Checking against a range makes code simpler by removing test subject duplication. Example: 'fun checkMonth(month: Int): Boolean {\n return month >= 1 && month <= 12\n }' The quick-fix replaces the comparison-based check with a range one: 'fun checkMonth(month: Int): Boolean {\n return month in 1..12\n }'", + "markdown": "Reports two consecutive comparisons that can be converted to a range check.\n\nChecking against a range makes code simpler by removing test subject duplication.\n\n**Example:**\n\n\n fun checkMonth(month: Int): Boolean {\n return month >= 1 && month <= 12\n }\n\nThe quick-fix replaces the comparison-based check with a range one:\n\n\n fun checkMonth(month: Int): Boolean {\n return month in 1..12\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ConvertTwoComparisonsToRangeCheck", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ProhibitUseSiteTargetAnnotationsOnSuperTypesMigration", + "shortDescription": { + "text": "Meaningless annotations targets on superclass" + }, + "fullDescription": { + "text": "Reports meaningless annotation targets on superclasses since Kotlin 1.4. Annotation targets such as '@get:' are meaningless on superclasses and are prohibited. Example: 'interface Foo\n\n annotation class Ann\n\n class E : @field:Ann @get:Ann @set:Ann @setparam:Ann Foo' After the quick-fix is applied: 'interface Foo\n\n annotation class Ann\n\n class E : Foo' This inspection only reports if the Kotlin language level of the project or module is 1.4 or higher.", + "markdown": "Reports meaningless annotation targets on superclasses since Kotlin 1.4.\n\nAnnotation targets such as `@get:` are meaningless on superclasses and are prohibited.\n\n**Example:**\n\n\n interface Foo\n\n annotation class Ann\n\n class E : @field:Ann @get:Ann @set:Ann @setparam:Ann Foo\n\nAfter the quick-fix is applied:\n\n\n interface Foo\n\n annotation class Ann\n\n class E : Foo\n\nThis inspection only reports if the Kotlin language level of the project or module is 1.4 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "ProhibitUseSiteTargetAnnotationsOnSuperTypesMigration", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Migration", + "index": 16, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceWithEnumMap", + "shortDescription": { + "text": "'HashMap' can be replaced with 'EnumMap'" + }, + "fullDescription": { + "text": "Reports 'hashMapOf' function or 'HashMap' constructor calls that can be replaced with an 'EnumMap' constructor call. Using 'EnumMap' constructor makes your code simpler. The quick-fix replaces the function call with the 'EnumMap' constructor call. Example: 'enum class E {\n A, B\n }\n\n fun getMap(): Map = hashMapOf()' After the quick-fix is applied: 'enum class E {\n A, B\n }\n\n fun getMap(): Map = EnumMap(E::class.java)'", + "markdown": "Reports `hashMapOf` function or `HashMap` constructor calls that can be replaced with an `EnumMap` constructor call.\n\nUsing `EnumMap` constructor makes your code simpler.\n\nThe quick-fix replaces the function call with the `EnumMap` constructor call.\n\n**Example:**\n\n\n enum class E {\n A, B\n }\n\n fun getMap(): Map = hashMapOf()\n\nAfter the quick-fix is applied:\n\n\n enum class E {\n A, B\n }\n\n fun getMap(): Map = EnumMap(E::class.java)\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "ReplaceWithEnumMap", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Other problems", + "index": 54, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LocalVariableName", + "shortDescription": { + "text": "Local variable naming convention" + }, + "fullDescription": { + "text": "Reports local variables that do not follow the naming conventions. You can specify the required pattern in the inspection options. Recommended naming conventions: it has to start with a lowercase letter, use camel case and no underscores. Example: 'fun fibonacciNumber(index: Int): Long = when(index) {\n 0 -> 0\n else -> {\n // does not follow naming conventions: contains underscore symbol (`_`)\n var number_one: Long = 0\n // does not follow naming conventions: starts with an uppercase letter\n var NUMBER_TWO: Long = 1\n // follow naming conventions: starts with a lowercase letter, use camel case and no underscores.\n var numberThree: Long = number_one + NUMBER_TWO\n\n for(currentIndex in 2..index) {\n numberThree = number_one + NUMBER_TWO\n number_one = NUMBER_TWO\n NUMBER_TWO = numberThree\n }\n numberThree\n }\n }'", + "markdown": "Reports local variables that do not follow the naming conventions.\n\nYou can specify the required pattern in the inspection options.\n\n[Recommended naming conventions](https://kotlinlang.org/docs/coding-conventions.html#function-names): it has to start with a lowercase letter, use camel case and no underscores.\n\n**Example:**\n\n\n fun fibonacciNumber(index: Int): Long = when(index) {\n 0 -> 0\n else -> {\n // does not follow naming conventions: contains underscore symbol (`_`)\n var number_one: Long = 0\n // does not follow naming conventions: starts with an uppercase letter\n var NUMBER_TWO: Long = 1\n // follow naming conventions: starts with a lowercase letter, use camel case and no underscores.\n var numberThree: Long = number_one + NUMBER_TWO\n\n for(currentIndex in 2..index) {\n numberThree = number_one + NUMBER_TWO\n number_one = NUMBER_TWO\n NUMBER_TWO = numberThree\n }\n numberThree\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "LocalVariableName", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Naming conventions", + "index": 49, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceUntilWithRangeUntil", + "shortDescription": { + "text": "Replace 'until' with '..<' operator" + }, + "fullDescription": { + "text": "Reports 'until' that can be replaced with '..<' operator. Every 'until' to '..<' replacement doesn't change the semantic in any way. The UX research shows that developers make ~20-30% fewer errors when reading code containing '..<' compared to 'until'. Example: 'fun main(args: Array) {\n for (index in 0 until args.size) {\n println(index)\n }\n }' After the quick-fix is applied: 'fun main(args: Array) {\n for (index in 0..) {\n for (index in 0 until args.size) {\n println(index)\n }\n }\n\nAfter the quick-fix is applied:\n\n\n fun main(args: Array) {\n for (index in 0..' which inherits from 'List' instead of 'Array'). Due to this in some cases quick fix inserts extra '.toTypedArray()' conversion to not break the code, but for most common cases replacement will be done without it (e.g. in 'for' loop). Example: 'enum class Version {\n V1, V2\n }\n\n Version.values().forEach { /* .. */ }\n val firstVersion = Version.values()[0]\n functionExpectingArray(Version.values())' After the quick-fix is applied: 'enum class Version {\n V1, V2\n }\n\n Version.entries.forEach { /* .. */ }\n val firstVersion = Version.entries[0]\n functionExpectingArray(Version.entries.toTypedArray())'", + "markdown": "Reports calls from Kotlin to `values()` method in enum classes that can be replaced with `entries` property read.\n\n\nUse of `Enum.entries` may improve performance of your code.\n\n\nThe quick-fix replaces `values()` with `entries`.\n\n\n**More details:** [KT-48872 Provide modern and performant replacement for Enum.values()](https://youtrack.jetbrains.com/issue/KT-48872)\n\n\n**Note:** `entries` property type is different from the return type of `values()` method\n(`EnumEntries` which inherits from `List` instead of `Array`).\nDue to this in some cases quick fix inserts extra `.toTypedArray()` conversion to not break the code, but\nfor most common cases replacement will be done without it (e.g. in `for` loop).\n\n**Example:**\n\n\n enum class Version {\n V1, V2\n }\n\n Version.values().forEach { /* .. */ }\n val firstVersion = Version.values()[0]\n functionExpectingArray(Version.values())\n\nAfter the quick-fix is applied:\n\n\n enum class Version {\n V1, V2\n }\n\n Version.entries.forEach { /* .. */ }\n val firstVersion = Version.entries[0]\n functionExpectingArray(Version.entries.toTypedArray())\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "EnumValuesSoftDeprecate", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Other problems", + "index": 54, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RemoveRedundantQualifierName", + "shortDescription": { + "text": "Redundant qualifier name" + }, + "fullDescription": { + "text": "Reports redundant qualifiers (or their parts) on class names, functions, and properties. A fully qualified name is an unambiguous identifier that specifies which object, function, or property a call refers to. In the contexts where the name can be shortened, the inspection informs on the opportunity and the associated 'Remove redundant qualifier name' quick-fix allows amending the code. Examples: 'package my.simple.name\n import kotlin.Int.Companion.MAX_VALUE\n\n class Foo\n\n fun main() {\n val a = my.simple.name.Foo() // 'Foo' resides in the declared 'my.simple.name' package, qualifier is redundant\n val b = kotlin.Int.MAX_VALUE // Can be replaced with 'MAX_VALUE' since it's imported\n val c = kotlin.Double.MAX_VALUE // Can be replaced with 'Double.MAX_VALUE' since built-in types are imported automatically\n }' After the quick-fix is applied: 'package my.simple.name\n import kotlin.Int.Companion.MAX_VALUE\n\n class Foo\n\n fun main() {\n val a = Foo()\n val b = MAX_VALUE\n val c = Double.MAX_VALUE\n }'", + "markdown": "Reports redundant qualifiers (or their parts) on class names, functions, and properties.\n\n\nA fully qualified name is an unambiguous identifier that specifies which object, function, or property a call refers to.\nIn the contexts where the name can be shortened, the inspection informs on the opportunity and the associated\n'Remove redundant qualifier name' quick-fix allows amending the code.\n\n**Examples:**\n\n\n package my.simple.name\n import kotlin.Int.Companion.MAX_VALUE\n\n class Foo\n\n fun main() {\n val a = my.simple.name.Foo() // 'Foo' resides in the declared 'my.simple.name' package, qualifier is redundant\n val b = kotlin.Int.MAX_VALUE // Can be replaced with 'MAX_VALUE' since it's imported\n val c = kotlin.Double.MAX_VALUE // Can be replaced with 'Double.MAX_VALUE' since built-in types are imported automatically\n }\n\nAfter the quick-fix is applied:\n\n\n package my.simple.name\n import kotlin.Int.Companion.MAX_VALUE\n\n class Foo\n\n fun main() {\n val a = Foo()\n val b = MAX_VALUE\n val c = Double.MAX_VALUE\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RemoveRedundantQualifierName", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RemoveCurlyBracesFromTemplate", + "shortDescription": { + "text": "Redundant curly braces in string template" + }, + "fullDescription": { + "text": "Reports usages of curly braces in string templates around simple identifiers. Use the 'Remove curly braces' quick-fix to remove the redundant braces. Examples: 'fun redundant() {\n val x = 4\n val y = \"${x}\" // <== redundant\n }\n\n fun correctUsage() {\n val x = \"x\"\n val y = \"${x.length}\" // <== Ok\n }' After the quick-fix is applied: 'fun redundant() {\n val x = 4\n val y = \"$x\"\n }\n\n fun correctUsage() {\n val x = \"x\" <== Updated\n val y = \"${x.length}\"\n }'", + "markdown": "Reports usages of curly braces in string templates around simple identifiers.\n\nUse the 'Remove curly braces' quick-fix to remove the redundant braces.\n\n**Examples:**\n\n\n fun redundant() {\n val x = 4\n val y = \"${x}\" // <== redundant\n }\n\n fun correctUsage() {\n val x = \"x\"\n val y = \"${x.length}\" // <== Ok\n }\n\nAfter the quick-fix is applied:\n\n\n fun redundant() {\n val x = 4\n val y = \"$x\"\n }\n\n fun correctUsage() {\n val x = \"x\" <== Updated\n val y = \"${x.length}\"\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RemoveCurlyBracesFromTemplate", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceSubstringWithIndexingOperation", + "shortDescription": { + "text": "'substring' call should be replaced with indexing operator" + }, + "fullDescription": { + "text": "Reports calls like '\"abc\".substring(0, 1)' that can be replaced with '\"abc\"[0]'. Obtaining the element by index makes your code simpler. The quick-fix replaces the 'substring' call with the indexing operator. Example: 'fun foo() {\n \"abc\".substring(0, 1)\n }' After the quick-fix is applied: 'fun foo() {\n \"abc\"[0]\n }'", + "markdown": "Reports calls like `\"abc\".substring(0, 1)` that can be replaced with `\"abc\"[0]`.\n\nObtaining the element by index makes your code simpler.\n\nThe quick-fix replaces the `substring` call with the indexing operator.\n\n**Example:**\n\n\n fun foo() {\n \"abc\".substring(0, 1)\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo() {\n \"abc\"[0]\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceSubstringWithIndexingOperation", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspiciousCollectionReassignment", + "shortDescription": { + "text": "Augmented assignment creates a new collection under the hood" + }, + "fullDescription": { + "text": "Reports augmented assignment ('+=') expressions on a read-only 'Collection'. Augmented assignment ('+=') expression on a read-only 'Collection' temporarily allocates a new collection, which may hurt performance. Change type to mutable quick-fix can be used to amend the code automatically. Example: 'fun test() {\n var list = listOf(0)\n list += 42 // A new list is allocated here, equivalent to list = list + 42\n }' After the quick-fix is applied: 'fun test() {\n val list = mutableListOf(0)\n list += 42\n }'", + "markdown": "Reports augmented assignment (`+=`) expressions on a read-only `Collection`.\n\nAugmented assignment (`+=`) expression on a read-only `Collection` temporarily allocates a new collection,\nwhich may hurt performance.\n\n**Change type to mutable** quick-fix can be used to amend the code automatically.\n\nExample:\n\n\n fun test() {\n var list = listOf(0)\n list += 42 // A new list is allocated here, equivalent to list = list + 42\n }\n\nAfter the quick-fix is applied:\n\n\n fun test() {\n val list = mutableListOf(0)\n list += 42\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "note", + "parameters": { + "suppressToolId": "SuspiciousCollectionReassignment", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MoveLambdaOutsideParentheses", + "shortDescription": { + "text": "Lambda argument inside parentheses" + }, + "fullDescription": { + "text": "Reports lambda expressions in parentheses which can be moved outside. Example: 'fun square(a: Int, b: (Int) -> Int) {\n b(a * a)\n}\n\nfun foo() {\n square(2, { it })\n}' After the quick-fix is applied: 'fun foo() {\n square(2){ it }\n}'", + "markdown": "Reports lambda expressions in parentheses which can be moved outside.\n\n**Example:**\n\n\n fun square(a: Int, b: (Int) -> Int) {\n b(a * a)\n }\n\n fun foo() {\n square(2, { it })\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo() {\n square(2){ it }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "MoveLambdaOutsideParentheses", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantNotNullExtensionReceiverOfInline", + "shortDescription": { + "text": "'inline fun' extension receiver can be explicitly nullable until Kotlin 1.2" + }, + "fullDescription": { + "text": "Reports inline functions with non-nullable extension receivers which don't use the fact that extension receiver is not nullable. Before Kotlin 1.2, calls of 'inline fun' with flexible nullable extension receiver (a platform type with an unknown nullability) did not include nullability checks in bytecode. Since Kotlin 1.2, nullability checks are included into the bytecode (see KT-12899). Thus functions which do not use the fact that extension receiver is not nullable are dangerous in Kotlin until 1.2 and it's recommended to make such functions to have nullable receiver. Example: 'inline fun String.greet() {\n println(\"Hello, $this!\")\n }\n\n fun main() {\n // `System.getProperty` returns not denotable `String!` type\n val user = System.getProperty(\"user.name\")\n user.greet()\n }' After the quick-fix is applied: 'inline fun String.greet() {\n println(\"Hello, $this!\")\n }\n\n fun main() {\n // `System.getProperty` returns not denotable `String!` type\n val user = System.getProperty(\"user.name\")\n user.greet()\n }' This inspection only reports if the Kotlin language level of the project or module is lower than 1.2.", + "markdown": "Reports inline functions with non-nullable extension receivers which don't use the fact that extension receiver is not nullable.\n\n\nBefore Kotlin 1.2, calls of `inline fun` with flexible nullable extension receiver (a platform type with an unknown\nnullability) did not include nullability checks in bytecode. Since Kotlin 1.2, nullability checks are included into the bytecode\n(see [KT-12899](https://youtrack.jetbrains.com/issue/KT-12899)).\n\n\nThus functions which do not use the fact that extension receiver is not nullable are dangerous in Kotlin until 1.2 and it's\nrecommended to make such functions to have nullable receiver.\n\n**Example:**\n\n\n inline fun String.greet() {\n println(\"Hello, $this!\")\n }\n\n fun main() {\n // `System.getProperty` returns not denotable `String!` type\n val user = System.getProperty(\"user.name\")\n user.greet()\n }\n\nAfter the quick-fix is applied:\n\n\n inline fun String.greet() {\n println(\"Hello, $this!\")\n }\n\n fun main() {\n // `System.getProperty` returns not denotable `String!` type\n val user = System.getProperty(\"user.name\")\n user.greet()\n }\n\nThis inspection only reports if the Kotlin language level of the project or module is lower than 1.2." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RedundantNotNullExtensionReceiverOfInline", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Java interop issues", + "index": 70, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantElvisReturnNull", + "shortDescription": { + "text": "Redundant '?: return null'" + }, + "fullDescription": { + "text": "Reports redundant '?: return null' Example: 'fun foo(): Int? {\n ...\n }\n\n fun test() : Int? {\n return foo() ?: return null\n }' After the quick-fix is applied: 'fun foo(): Int? {\n ...\n }\n\n fun test() : Int? {\n return foo()\n }'", + "markdown": "Reports redundant `?: return null`\n\n**Example:**\n\n\n fun foo(): Int? {\n ...\n }\n\n fun test() : Int? {\n return foo() ?: return null\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(): Int? {\n ...\n }\n\n fun test() : Int? {\n return foo()\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantElvisReturnNull", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ProhibitTypeParametersForLocalVariablesMigration", + "shortDescription": { + "text": "Local variable with type parameters" + }, + "fullDescription": { + "text": "Reports local variables with type parameters. A type parameter for a local variable doesn't make sense because it can't be specialized. Example: 'fun main() {\n val x = \"\"\n }' After the quick-fix is applied: 'fun main() {\n val x = \"\"\n }' This inspection only reports if the Kotlin language level of the project or module is 1.4 or higher.", + "markdown": "Reports local variables with type parameters.\n\nA type parameter for a local variable doesn't make sense because it can't be specialized.\n\n**Example:**\n\n\n fun main() {\n val x = \"\"\n }\n\nAfter the quick-fix is applied:\n\n\n fun main() {\n val x = \"\"\n }\n\nThis inspection only reports if the Kotlin language level of the project or module is 1.4 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "ProhibitTypeParametersForLocalVariablesMigration", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Migration", + "index": 16, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PrivatePropertyName", + "shortDescription": { + "text": "Private property naming convention" + }, + "fullDescription": { + "text": "Reports private property names that do not follow the recommended naming conventions. Consistent naming allows for easier code reading and understanding. According to the Kotlin official style guide, private property names should start with a lowercase letter and use camel case. Optionally, underscore prefix is allowed but only for private properties. It is possible to introduce other naming rules by changing the \"Pattern\" regular expression. Example: 'val _My_Cool_Property = \"\"' The quick-fix renames the class according to the Kotlin naming conventions: 'val _myCoolProperty = \"\"'", + "markdown": "Reports private property names that do not follow the recommended naming conventions.\n\n\nConsistent naming allows for easier code reading and understanding.\nAccording to the [Kotlin official style guide](https://kotlinlang.org/docs/coding-conventions.html#naming-rules),\nprivate property names should start with a lowercase letter and use camel case.\nOptionally, underscore prefix is allowed but only for **private** properties.\n\nIt is possible to introduce other naming rules by changing the \"Pattern\" regular expression.\n\n**Example:**\n\n\n val _My_Cool_Property = \"\"\n\nThe quick-fix renames the class according to the Kotlin naming conventions:\n\n\n val _myCoolProperty = \"\"\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "PrivatePropertyName", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Naming conventions", + "index": 49, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KotlinJvmAnnotationInJava", + "shortDescription": { + "text": "Kotlin JVM annotation in Java" + }, + "fullDescription": { + "text": "Reports useless Kotlin JVM annotations in Java code. Example: 'import kotlin.jvm.Volatile;\n\n public class Test {\n @Volatile\n public int i;\n }'", + "markdown": "Reports useless Kotlin JVM annotations in Java code.\n\n**Example:**\n\n\n import kotlin.jvm.Volatile;\n\n public class Test {\n @Volatile\n public int i;\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "KotlinJvmAnnotationInJava", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Java interop issues", + "index": 70, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ObsoleteKotlinJsPackages", + "shortDescription": { + "text": "'kotlin.browser' and 'kotlin.dom' packages are deprecated since 1.4" + }, + "fullDescription": { + "text": "Reports usages of 'kotlin.dom' and 'kotlin.browser' packages. These packages were moved to 'kotlinx.dom' and 'kotlinx.browser' respectively in Kotlin 1.4+.", + "markdown": "Reports usages of `kotlin.dom` and `kotlin.browser` packages.\n\nThese packages were moved to `kotlinx.dom` and `kotlinx.browser`\nrespectively in Kotlin 1.4+." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "ObsoleteKotlinJsPackages", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Migration", + "index": 16, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CascadeIf", + "shortDescription": { + "text": "Cascade 'if' can be replaced with 'when'" + }, + "fullDescription": { + "text": "Reports 'if' statements with three or more branches that can be replaced with the 'when' expression. Example: 'fun checkIdentifier(id: String) {\n fun Char.isIdentifierStart() = this in 'A'..'z'\n fun Char.isIdentifierPart() = isIdentifierStart() || this in '0'..'9'\n\n if (id.isEmpty()) {\n print(\"Identifier is empty\")\n } else if (!id.first().isIdentifierStart()) {\n print(\"Identifier should start with a letter\")\n } else if (!id.subSequence(1, id.length).all(Char::isIdentifierPart)) {\n print(\"Identifier should contain only letters and numbers\")\n }\n }' The quick-fix converts the 'if' expression to 'when': 'fun checkIdentifier(id: String) {\n fun Char.isIdentifierStart() = this in 'A'..'z'\n fun Char.isIdentifierPart() = isIdentifierStart() || this in '0'..'9'\n\n when {\n id.isEmpty() -> {\n print(\"Identifier is empty\")\n }\n !id.first().isIdentifierStart() -> {\n print(\"Identifier should start with a letter\")\n }\n !id.subSequence(1, id.length).all(Char::isIdentifierPart) -> {\n print(\"Identifier should contain only letters and numbers\")\n }\n }\n }'", + "markdown": "Reports `if` statements with three or more branches that can be replaced with the `when` expression.\n\n**Example:**\n\n\n fun checkIdentifier(id: String) {\n fun Char.isIdentifierStart() = this in 'A'..'z'\n fun Char.isIdentifierPart() = isIdentifierStart() || this in '0'..'9'\n\n if (id.isEmpty()) {\n print(\"Identifier is empty\")\n } else if (!id.first().isIdentifierStart()) {\n print(\"Identifier should start with a letter\")\n } else if (!id.subSequence(1, id.length).all(Char::isIdentifierPart)) {\n print(\"Identifier should contain only letters and numbers\")\n }\n }\n\nThe quick-fix converts the `if` expression to `when`:\n\n\n fun checkIdentifier(id: String) {\n fun Char.isIdentifierStart() = this in 'A'..'z'\n fun Char.isIdentifierPart() = isIdentifierStart() || this in '0'..'9'\n\n when {\n id.isEmpty() -> {\n print(\"Identifier is empty\")\n }\n !id.first().isIdentifierStart() -> {\n print(\"Identifier should start with a letter\")\n }\n !id.subSequence(1, id.length).all(Char::isIdentifierPart) -> {\n print(\"Identifier should contain only letters and numbers\")\n }\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "CascadeIf", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EmptyRange", + "shortDescription": { + "text": "Range with start greater than endInclusive is empty" + }, + "fullDescription": { + "text": "Reports ranges that are empty because the 'start' value is greater than the 'endInclusive' value. Example: 'val range = 2..1' The quick-fix changes the '..' operator to 'downTo': 'val range = 2 downTo 1'", + "markdown": "Reports ranges that are empty because the `start` value is greater than the `endInclusive` value.\n\n**Example:**\n\n\n val range = 2..1\n\nThe quick-fix changes the `..` operator to `downTo`:\n\n\n val range = 2 downTo 1\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "EmptyRange", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TestFunctionName", + "shortDescription": { + "text": "Test function naming convention" + }, + "fullDescription": { + "text": "Reports test function names that do not follow the recommended naming conventions.", + "markdown": "Reports test function names that do not follow the [recommended naming conventions](https://kotlinlang.org/docs/coding-conventions.html#names-for-test-methods)." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "TestFunctionName", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Naming conventions", + "index": 49, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RecursivePropertyAccessor", + "shortDescription": { + "text": "Recursive property accessor" + }, + "fullDescription": { + "text": "Reports recursive property accessor calls which can end up with a 'StackOverflowError'. Such calls are usually confused with backing field access. Example: 'var counter: Int = 0\n set(value) {\n counter = if (value < 0) 0 else value\n }' After the quick-fix is applied: 'var counter: Int = 0\n set(value) {\n field = if (value < 0) 0 else value\n }'", + "markdown": "Reports recursive property accessor calls which can end up with a `StackOverflowError`.\nSuch calls are usually confused with backing field access.\n\n**Example:**\n\n\n var counter: Int = 0\n set(value) {\n counter = if (value < 0) 0 else value\n }\n\nAfter the quick-fix is applied:\n\n\n var counter: Int = 0\n set(value) {\n field = if (value < 0) 0 else value\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "RecursivePropertyAccessor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonExternalClassifierExtendingStateOrProps", + "shortDescription": { + "text": "Non-external classifier extending State or Props" + }, + "fullDescription": { + "text": "Reports non-external classifier extending State or Props. Read more in the migration guide.", + "markdown": "Reports non-external classifier extending State or Props. Read more in the [migration guide](https://kotlinlang.org/docs/js-ir-migration.html#convert-js-and-react-related-classes-and-interfaces-to-external-interfaces)." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "NonExternalClassifierExtendingStateOrProps", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/React/Probable bugs", + "index": 124, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OptionalExpectation", + "shortDescription": { + "text": "Optionally expected annotation has no actual annotation" + }, + "fullDescription": { + "text": "Reports optionally expected annotations without actual annotation in some platform modules. Example: '// common code\n@OptionalExpectation\nexpect annotation class JvmName(val name: String)\n\n@JvmName(name = \"JvmFoo\")\nfun foo() { }\n\n// jvm code\nactual annotation class JvmName(val name: String)' The inspection also reports cases when 'actual annotation class JvmName' is omitted for non-JVM platforms (for example, Native).", + "markdown": "Reports optionally expected annotations without actual annotation in some platform modules.\n\n**Example:**\n\n // common code\n @OptionalExpectation\n expect annotation class JvmName(val name: String)\n\n @JvmName(name = \"JvmFoo\")\n fun foo() { }\n\n // jvm code\n actual annotation class JvmName(val name: String)\n\nThe inspection also reports cases when `actual annotation class JvmName` is omitted for non-JVM platforms (for example, Native)." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "OptionalExpectation", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DestructuringWrongName", + "shortDescription": { + "text": "Variable in destructuring declaration uses name of a wrong data class property" + }, + "fullDescription": { + "text": "Reports entries of destructuring declarations that match the name of a different property of the destructured data class. Example: 'data class Foo(val a: String, val b: Int, val c: String)\n\n fun bar(f: Foo) {\n val (a, c) = f\n }' The quick-fix changes variable's name to match the name of the corresponding class field: 'data class Foo(val a: String, val b: Int, val c: String)\n\n fun bar(f: Foo) {\n val (a, b) = f\n }'", + "markdown": "Reports entries of destructuring declarations that match the name of a different property of the destructured data class.\n\n**Example:**\n\n\n data class Foo(val a: String, val b: Int, val c: String)\n\n fun bar(f: Foo) {\n val (a, c) = f\n }\n\nThe quick-fix changes variable's name to match the name of the corresponding class field:\n\n\n data class Foo(val a: String, val b: Int, val c: String)\n\n fun bar(f: Foo) {\n val (a, b) = f\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "DestructuringWrongName", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IfThenToSafeAccess", + "shortDescription": { + "text": "If-Then foldable to '?.'" + }, + "fullDescription": { + "text": "Reports 'if-then' expressions that can be folded into safe-access ('?.') expressions. Example: 'fun bar(x: String) = \"\"\n\n fun foo(a: String?) {\n if (a != null) bar(a) else null\n }' The quick fix converts the 'if-then' expression into a safe-access ('?.') expression: 'fun bar(x: String) = \"\"\n\n fun foo(a: String?) {\n a?.let { bar(it) }\n }'", + "markdown": "Reports `if-then` expressions that can be folded into safe-access (`?.`) expressions.\n\n**Example:**\n\n\n fun bar(x: String) = \"\"\n\n fun foo(a: String?) {\n if (a != null) bar(a) else null\n }\n\nThe quick fix converts the `if-then` expression into a safe-access (`?.`) expression:\n\n\n fun bar(x: String) = \"\"\n\n fun foo(a: String?) {\n a?.let { bar(it) }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "IfThenToSafeAccess", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RestrictReturnStatementTargetMigration", + "shortDescription": { + "text": "Target label does not denote a function since 1.4" + }, + "fullDescription": { + "text": "Reports labels that don't points to a functions. It's forbidden to declare a target label that does not denote a function. The quick-fix removes the label. Example: 'fun testValLabelInReturn() {\n L@ val fn = { return@L }\n fn()\n }' After the quick-fix is applied: 'fun testValLabelInReturn() {\n L@ val fn = { return }\n fn()\n }' This inspection only reports if the language level of the project or module is 1.4 or higher.", + "markdown": "Reports labels that don't points to a functions.\n\nIt's forbidden to declare a target label that does not denote a function.\n\nThe quick-fix removes the label.\n\n**Example:**\n\n\n fun testValLabelInReturn() {\n L@ val fn = { return@L }\n fn()\n }\n\nAfter the quick-fix is applied:\n\n\n fun testValLabelInReturn() {\n L@ val fn = { return }\n fn()\n }\n\nThis inspection only reports if the language level of the project or module is 1.4 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "RestrictReturnStatementTargetMigration", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Migration", + "index": 16, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantUnitExpression", + "shortDescription": { + "text": "Redundant 'Unit'" + }, + "fullDescription": { + "text": "Reports redundant 'Unit' expressions. 'Unit' in Kotlin can be used as the return type of functions that do not return anything meaningful. The 'Unit' type has only one possible value, which is the 'Unit' object. Examples: 'fun redundantA(): Unit {\n return Unit // redundant, 'Unit' is returned by default and matches the expected return type\n }\n\n fun requiredA(condition: Boolean): Any {\n if (condition) return \"hello\"\n return Unit // explicit 'Unit' is required since the expected type is 'Any'\n }\n\n fun redundantB(condition: Boolean): Any = if (condition) {\n fun ancillary(): Int = 1\n println(\"${ancillary()}\")\n Unit // redundant since the last expression is already of type 'Unit'\n } else {\n println(\"else\")\n }\n\n fun requiredB(condition: Boolean): Any = if (condition) {\n 1024\n Unit // required, otherwise '1024' (Int) would be the return value\n } else {\n println(\"else\")\n }'", + "markdown": "Reports redundant `Unit` expressions.\n\n\n`Unit` in Kotlin can be used as the return type of functions that do not return anything meaningful.\nThe `Unit` type has only one possible value, which is the `Unit` object.\n\n**Examples:**\n\n\n fun redundantA(): Unit {\n return Unit // redundant, 'Unit' is returned by default and matches the expected return type\n }\n\n fun requiredA(condition: Boolean): Any {\n if (condition) return \"hello\"\n return Unit // explicit 'Unit' is required since the expected type is 'Any'\n }\n\n fun redundantB(condition: Boolean): Any = if (condition) {\n fun ancillary(): Int = 1\n println(\"${ancillary()}\")\n Unit // redundant since the last expression is already of type 'Unit'\n } else {\n println(\"else\")\n }\n\n fun requiredB(condition: Boolean): Any = if (condition) {\n 1024\n Unit // required, otherwise '1024' (Int) would be the return value\n } else {\n println(\"else\")\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantUnitExpression", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PlatformExtensionReceiverOfInline", + "shortDescription": { + "text": "'inline fun' with nullable receiver until Kotlin 1.2" + }, + "fullDescription": { + "text": "Reports potentially unsafe calls of inline functions with flexible nullable (platform type with unknown nullability) extension receivers. Before Kotlin 1.2, calls of 'inline fun' with flexible nullable extension receiver (a platform type with an unknown nullability) did not include nullability checks in bytecode. Since Kotlin 1.2, nullability checks are included into the bytecode (see KT-12899). It's recommended to add an explicit '!!' you want an exception to be thrown, or consider changing the function's receiver type to nullable if it should work without exceptions. Example: 'inline fun String.removePrefix(prefix: String): String {\n return this.substring(prefix.length)\n }\n\n fun main() {\n // `System.getProperty` returns not denotable `String!` type\n val property = System.getProperty(\"user.dir\")\n println(property.removePrefix(\"/home\"))\n }' After the quick-fix is applied: 'inline fun String.removePrefix(prefix: String): String {\n return this.substring(prefix.length)\n }\n\n fun main() {\n // `System.getProperty` returns not denotable `String!` type\n val property = System.getProperty(\"user.dir\")\n println(property!!.removePrefix(\"/home\"))\n }' This inspection only reports if the Kotlin language level of the project or module is lower than 1.2.", + "markdown": "Reports potentially unsafe calls of inline functions with flexible nullable (platform type with unknown nullability) extension receivers.\n\n\nBefore Kotlin 1.2, calls of `inline fun` with flexible nullable extension receiver (a platform type with an unknown\nnullability) did not include nullability checks in bytecode. Since Kotlin 1.2, nullability checks are included into the bytecode\n(see [KT-12899](https://youtrack.jetbrains.com/issue/KT-12899)).\n\n\nIt's recommended to add an explicit `!!` you want an exception to be thrown,\nor consider changing the function's receiver type to nullable if it should work without exceptions.\n\n**Example:**\n\n\n inline fun String.removePrefix(prefix: String): String {\n return this.substring(prefix.length)\n }\n\n fun main() {\n // `System.getProperty` returns not denotable `String!` type\n val property = System.getProperty(\"user.dir\")\n println(property.removePrefix(\"/home\"))\n }\n\nAfter the quick-fix is applied:\n\n\n inline fun String.removePrefix(prefix: String): String {\n return this.substring(prefix.length)\n }\n\n fun main() {\n // `System.getProperty` returns not denotable `String!` type\n val property = System.getProperty(\"user.dir\")\n println(property!!.removePrefix(\"/home\"))\n }\n\nThis inspection only reports if the Kotlin language level of the project or module is lower than 1.2." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "PlatformExtensionReceiverOfInline", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Java interop issues", + "index": 70, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspiciousEqualsCombination", + "shortDescription": { + "text": "Suspicious combination of == and ===" + }, + "fullDescription": { + "text": "Reports '==' and '===' comparisons that are both used on the same variable within a single expression. Due to similarities '==' and '===' could be mixed without notice, and it takes a close look to check that '==' used instead of '===' Example: 'if (type === FIELD || type == METHOD || type == ANNOTATION_METHOD || // Note that \"==\" is used incorrectly\n type === LAMBDA_EXPRESSION) return'", + "markdown": "Reports `==` and `===` comparisons that are both used on the same variable within a single expression.\n\nDue to similarities `==` and `===` could be mixed without notice, and\nit takes a close look to check that `==` used instead of `===`\n\nExample:\n\n\n if (type === FIELD || type == METHOD || type == ANNOTATION_METHOD || // Note that \"==\" is used incorrectly\n type === LAMBDA_EXPRESSION) return\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "note", + "parameters": { + "suppressToolId": "SuspiciousEqualsCombination", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MigrateDiagnosticSuppression", + "shortDescription": { + "text": "Diagnostic name should be replaced" + }, + "fullDescription": { + "text": "Reports suppressions with old diagnostic names, for example '@Suppress(\"HEADER_WITHOUT_IMPLEMENTATION\")'. Some of diagnostics from Kotlin 1.2 and earlier are now obsolete, making such suppressions redundant. Example: '@Suppress(\"HEADER_DECLARATION_WITH_BODY\")\nexpect fun connection() {\n // ...\n}' After the quick-fix is applied: '@Suppress(\"EXPECTED_DECLARATION_WITH_BODY\")\nexpect fun connection() {\n // ...\n}'", + "markdown": "Reports suppressions with old diagnostic names, for example `@Suppress(\"HEADER_WITHOUT_IMPLEMENTATION\")`.\n\n\nSome of diagnostics from Kotlin 1.2 and earlier are now obsolete, making such suppressions redundant.\n\n**Example:**\n\n\n @Suppress(\"HEADER_DECLARATION_WITH_BODY\")\n expect fun connection() {\n // ...\n }\n\nAfter the quick-fix is applied:\n\n\n @Suppress(\"EXPECTED_DECLARATION_WITH_BODY\")\n expect fun connection() {\n // ...\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "MigrateDiagnosticSuppression", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Other problems", + "index": 54, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnusedDataClassCopyResult", + "shortDescription": { + "text": "Unused result of data class copy" + }, + "fullDescription": { + "text": "Reports calls to data class 'copy' function without using its result.", + "markdown": "Reports calls to data class `copy` function without using its result." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "UnusedDataClassCopyResult", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonNullableBooleanPropertyInExternalInterface", + "shortDescription": { + "text": "External interface contains non-nullable boolean property" + }, + "fullDescription": { + "text": "Reports non-nullable boolean properties in external interface. Read more in the migration guide.", + "markdown": "Reports non-nullable boolean properties in external interface. Read more in the [migration guide](https://kotlinlang.org/docs/js-ir-migration.html#make-boolean-properties-nullable-in-external-interfaces)." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonNullableBooleanPropertyInExternalInterface", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DeferredResultUnused", + "shortDescription": { + "text": "'@Deferred' result is unused" + }, + "fullDescription": { + "text": "Reports function calls with the 'Deferred' result type if the return value is not used. If the 'Deferred' return value is not used, the call site would not wait to complete this function. Example: 'fun calcEverythingAsync() = CompletableDeferred(42)\n\n fun usage() {\n calcEverythingAsync()\n }' The quick-fix provides a variable with the 'Deferred' initializer: 'fun calcEverythingAsync() = CompletableDeferred(42)\n\n fun usage() {\n val answer = calcEverythingAsync()\n }'", + "markdown": "Reports function calls with the `Deferred` result type if the return value is not used.\n\nIf the `Deferred` return value is not used, the call site would not wait to complete this function.\n\n**Example:**\n\n\n fun calcEverythingAsync() = CompletableDeferred(42)\n\n fun usage() {\n calcEverythingAsync()\n }\n\nThe quick-fix provides a variable with the `Deferred` initializer:\n\n\n fun calcEverythingAsync() = CompletableDeferred(42)\n\n fun usage() {\n val answer = calcEverythingAsync()\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "DeferredResultUnused", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantElseInIf", + "shortDescription": { + "text": "Redundant 'else' in 'if'" + }, + "fullDescription": { + "text": "Reports redundant 'else' in 'if' with 'return' Example: 'fun foo(arg: Boolean): Int {\n if (arg) return 0\n else { // This else is redundant, code in braces could be just shifted left\n someCode()\n }\n }' After the quick-fix is applied: 'fun foo(arg: Boolean): Int {\n if (arg) return 0\n someCode()\n }'", + "markdown": "Reports redundant `else` in `if` with `return`\n\n**Example:**\n\n\n fun foo(arg: Boolean): Int {\n if (arg) return 0\n else { // This else is redundant, code in braces could be just shifted left\n someCode()\n }\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(arg: Boolean): Int {\n if (arg) return 0\n someCode()\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RedundantElseInIf", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SelfReferenceConstructorParameter", + "shortDescription": { + "text": "Constructor can never be complete" + }, + "fullDescription": { + "text": "Reports constructors with a non-null self-reference parameter. Such constructors never instantiate a class. The quick-fix converts the parameter type to nullable. Example: 'class SelfRef(val ref: SelfRef)' After the quick-fix is applied: 'class SelfRef(val ref: SelfRef?)'", + "markdown": "Reports constructors with a non-null self-reference parameter.\n\nSuch constructors never instantiate a class.\n\nThe quick-fix converts the parameter type to nullable.\n\n**Example:**\n\n\n class SelfRef(val ref: SelfRef)\n\nAfter the quick-fix is applied:\n\n\n class SelfRef(val ref: SelfRef?)\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SelfReferenceConstructorParameter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JavaIoSerializableObjectMustHaveReadResolve", + "shortDescription": { + "text": "Serializable object must implement 'readResolve'" + }, + "fullDescription": { + "text": "Reports 'object's ('data object' including) that implement 'java.io.Serializable' but don't implement readResolve Example: 'import java.io.Serializable\n\n object Foo : Serializable' The quick fix implements 'readResolve' method: 'import java.io.Serializable\n\n object Foo : Serializable {\n private fun readResolve() = Foo\n }'", + "markdown": "Reports `object`s (`data object` including) that implement `java.io.Serializable` but don't implement\n[readResolve](https://docs.oracle.com/en/java/javase/11/docs/specs/serialization/input.html#the-readresolve-method)\n\n**Example:**\n\n\n import java.io.Serializable\n\n object Foo : Serializable\n\nThe quick fix implements `readResolve` method:\n\n\n import java.io.Serializable\n\n object Foo : Serializable {\n private fun readResolve() = Foo\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "JavaIoSerializableObjectMustHaveReadResolve", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplacePutWithAssignment", + "shortDescription": { + "text": "'map.put()' can be converted to assignment" + }, + "fullDescription": { + "text": "Reports 'map.put' function calls that can be replaced with indexing operator ('[]'). Using syntactic sugar makes your code simpler. The quick-fix replaces 'put' call with the assignment. Example: 'fun foo(map: MutableMap) {\n map.put(42, \"foo\")\n }' After the quick-fix is applied: 'fun foo(map: MutableMap) {\n map[42] = \"foo\"\n }'", + "markdown": "Reports `map.put` function calls that can be replaced with indexing operator (`[]`).\n\nUsing syntactic sugar makes your code simpler.\n\nThe quick-fix replaces `put` call with the assignment.\n\n**Example:**\n\n\n fun foo(map: MutableMap) {\n map.put(42, \"foo\")\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(map: MutableMap) {\n map[42] = \"foo\"\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplacePutWithAssignment", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MainFunctionReturnUnit", + "shortDescription": { + "text": "Main function should return 'Unit'" + }, + "fullDescription": { + "text": "Reports when a main function does not have a return type of 'Unit'. Example: 'fun main() = \"Hello world!\"'", + "markdown": "Reports when a main function does not have a return type of `Unit`.\n\n**Example:**\n`fun main() = \"Hello world!\"`" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "MainFunctionReturnUnit", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspiciousCallableReferenceInLambda", + "shortDescription": { + "text": "Suspicious callable reference used as lambda result" + }, + "fullDescription": { + "text": "Reports lambda expressions with one callable reference. It is a common error to replace a lambda with a callable reference without changing curly braces to parentheses. Example: 'listOf(1,2,3).map { it::toString }' After the quick-fix is applied: 'listOf(1,2,3).map(Int::toString)'", + "markdown": "Reports lambda expressions with one callable reference.\n\nIt is a common error to replace a lambda with a callable reference without changing curly braces to parentheses.\n\n**Example:**\n\n listOf(1,2,3).map { it::toString }\n\nAfter the quick-fix is applied:\n\n listOf(1,2,3).map(Int::toString)\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "note", + "parameters": { + "suppressToolId": "SuspiciousCallableReferenceInLambda", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KotlinDoubleNegation", + "shortDescription": { + "text": "Redundant double negation" + }, + "fullDescription": { + "text": "Reports redundant double negations. Example: 'val truth = !!true'", + "markdown": "Reports redundant double negations.\n\n**Example:**\n\n val truth = !!true\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "DoubleNegation", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FunctionName", + "shortDescription": { + "text": "Function naming convention" + }, + "fullDescription": { + "text": "Reports function names that do not follow the recommended naming conventions. Example: 'fun Foo() {}' To fix the problem change the name of the function to match the recommended naming conventions.", + "markdown": "Reports function names that do not follow the recommended naming conventions.\n\n**Example:**\n\n\n fun Foo() {}\n\nTo fix the problem change the name of the function to match the recommended naming conventions." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "FunctionName", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Naming conventions", + "index": 49, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConvertSecondaryConstructorToPrimary", + "shortDescription": { + "text": "Convert to primary constructor" + }, + "fullDescription": { + "text": "Reports a secondary constructor that can be replaced with a more concise primary constructor. Example: 'class User {\n val name: String\n\n constructor(name: String) {\n this.name = name\n }\n }' The quick-fix converts code automatically: 'class User(val name: String) {\n }'", + "markdown": "Reports a secondary constructor that can be replaced with a more concise primary constructor.\n\n**Example:**\n\n\n class User {\n val name: String\n\n constructor(name: String) {\n this.name = name\n }\n }\n\nThe quick-fix converts code automatically:\n\n\n class User(val name: String) {\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ConvertSecondaryConstructorToPrimary", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DifferentKotlinMavenVersion", + "shortDescription": { + "text": "Maven and IDE plugins versions are different" + }, + "fullDescription": { + "text": "Reports that Maven plugin version isn't properly supported in the current IDE plugin. This inconsistency may lead to different error reporting behavior in the IDE and the compiler", + "markdown": "Reports that Maven plugin version isn't properly supported in the current IDE plugin.\n\nThis inconsistency may lead to different error reporting behavior in the IDE and the compiler" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "DifferentKotlinMavenVersion", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin", + "index": 3, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceGetOrSet", + "shortDescription": { + "text": "Explicit 'get' or 'set' call" + }, + "fullDescription": { + "text": "Reports explicit calls to 'get' or 'set' functions which can be replaced by an indexing operator '[]'. Kotlin allows custom implementations for the predefined set of operators on types. To overload an operator, you can mark the corresponding function with the 'operator' modifier: 'operator fun get(index: Int) {}\n operator fun set(index: Int, value: Int) {}' The functions above correspond to the indexing operator. Example: 'class Test {\n operator fun get(i: Int): Int = 0\n }\n\n fun test() {\n Test().get(0) // replaceable 'get()'\n }' After the quick-fix is applied: 'class Test {\n operator fun get(i: Int): Int = 0\n }\n\n fun test() {\n Test()[0]\n }'", + "markdown": "Reports explicit calls to `get` or `set` functions which can be replaced by an indexing operator `[]`.\n\n\nKotlin allows custom implementations for the predefined set of operators on types.\nTo overload an operator, you can mark the corresponding function with the `operator` modifier:\n\n\n operator fun get(index: Int) {}\n operator fun set(index: Int, value: Int) {}\n \nThe functions above correspond to the indexing operator.\n\n**Example:**\n\n class Test {\n operator fun get(i: Int): Int = 0\n }\n\n fun test() {\n Test().get(0) // replaceable 'get()'\n }\n\nAfter the quick-fix is applied:\n\n class Test {\n operator fun get(i: Int): Int = 0\n }\n\n fun test() {\n Test()[0]\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceGetOrSet", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ProhibitRepeatedUseSiteTargetAnnotationsMigration", + "shortDescription": { + "text": "Repeated annotation which is not marked as '@Repeatable'" + }, + "fullDescription": { + "text": "Reports the repeated use of a non-'@Repeatable' annotation on property accessors. As a result of using non-'@Repeatable' annotation multiple times, both annotation usages will appear in the bytecode leading to an ambiguity in reflection calls. Since Kotlin 1.4 it's mandatory to either mark annotation as '@Repeatable' or not repeat the annotation, otherwise it will lead to compilation error. Example: 'annotation class Foo(val x: Int)\n\n @get:Foo(10)\n val a: String\n @Foo(20) get() = \"foo\" // annotation repeated twice but not marked as @Repeatable' This inspection only reports if the Kotlin language level of the project or module is 1.4 or higher.", + "markdown": "Reports the repeated use of a non-`@Repeatable` annotation on property accessors.\n\n\nAs a result of using non-`@Repeatable` annotation multiple times, both annotation usages\nwill appear in the bytecode leading to an ambiguity in reflection calls.\n\n\nSince Kotlin 1.4 it's mandatory to either mark annotation as `@Repeatable` or not\nrepeat the annotation, otherwise it will lead to compilation error.\n\n**Example:**\n\n\n annotation class Foo(val x: Int)\n\n @get:Foo(10)\n val a: String\n @Foo(20) get() = \"foo\" // annotation repeated twice but not marked as @Repeatable\n\nThis inspection only reports if the Kotlin language level of the project or module is 1.4 or higher." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "ProhibitRepeatedUseSiteTargetAnnotationsMigration", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Migration", + "index": 16, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantUnitReturnType", + "shortDescription": { + "text": "Redundant 'Unit' return type" + }, + "fullDescription": { + "text": "Reports a redundant 'Unit' return type which can be omitted.", + "markdown": "Reports a redundant `Unit` return type which can be omitted." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantUnitReturnType", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Destructure", + "shortDescription": { + "text": "Use destructuring declaration" + }, + "fullDescription": { + "text": "Reports declarations that can be destructured. Example: 'data class My(val first: String, val second: Int, val third: Boolean)\n\n fun foo(list: List) {\n list.forEach { my ->\n println(my.second)\n println(my.third)\n }\n }' The quick-fix destructures the declaration and introduces new variables with names from the corresponding class: 'data class My(val first: String, val second: Int, val third: Boolean)\n\n fun foo(list: List) {\n list.forEach { (_, second, third) ->\n println(second)\n println(third)\n }\n }'", + "markdown": "Reports declarations that can be destructured.\n\n**Example:**\n\n\n data class My(val first: String, val second: Int, val third: Boolean)\n\n fun foo(list: List) {\n list.forEach { my ->\n println(my.second)\n println(my.third)\n }\n }\n\nThe quick-fix destructures the declaration and introduces new variables with names from the corresponding class:\n\n\n data class My(val first: String, val second: Int, val third: Boolean)\n\n fun foo(list: List) {\n list.forEach { (_, second, third) ->\n println(second)\n println(third)\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "Destructure", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnusedReceiverParameter", + "shortDescription": { + "text": "Unused receiver parameter" + }, + "fullDescription": { + "text": "Reports receiver parameter of extension functions and properties that is not used. Remove redundant receiver parameter can be used to amend the code automatically.", + "markdown": "Reports receiver parameter of extension functions and properties that is not used.\n\n**Remove redundant receiver parameter** can be used to amend the code automatically." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnusedReceiverParameter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConvertTryFinallyToUseCall", + "shortDescription": { + "text": "Convert try / finally to use() call" + }, + "fullDescription": { + "text": "Reports a 'try-finally' block with 'resource.close()' in 'finally' which can be converted to a 'resource.use()' call. 'use()' is easier to read and less error-prone as there is no need in explicit 'close()' call. Example: 'fun example() {\n val reader = File(\"file.txt\").bufferedReader()\n try {\n reader.lineSequence().forEach(::print)\n } finally {\n reader.close()\n }\n }' After the quick-fix applied: 'fun example() {\n File(\"file.txt\").bufferedReader().use { reader ->\n reader.lineSequence().forEach(::print)\n }\n }'", + "markdown": "Reports a `try-finally` block with `resource.close()` in `finally` which can be converted to a `resource.use()` call.\n\n`use()` is easier to read and less error-prone as there is no need in explicit `close()` call.\n\n**Example:**\n\n\n fun example() {\n val reader = File(\"file.txt\").bufferedReader()\n try {\n reader.lineSequence().forEach(::print)\n } finally {\n reader.close()\n }\n }\n\nAfter the quick-fix applied:\n\n\n fun example() {\n File(\"file.txt\").bufferedReader().use { reader ->\n reader.lineSequence().forEach(::print)\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ConvertTryFinallyToUseCall", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KotlinRedundantOverride", + "shortDescription": { + "text": "Redundant overriding method" + }, + "fullDescription": { + "text": "Reports redundant overriding declarations. An override can be omitted if it does not modify the inherited signature semantics, for example, by changing visibility. Example: 'open class Foo {\n open fun singleExpression() {\n }\n }\n\n class Bar : Foo() {\n override fun singleExpression() = super.singleExpression()\n }' After the quick-fix is applied: 'class Bar : Foo() {\n }'", + "markdown": "Reports redundant overriding declarations.\n\n\nAn override can be omitted if it does not modify the inherited signature semantics, for example, by changing visibility.\n\n**Example:**\n\n\n open class Foo {\n open fun singleExpression() {\n }\n }\n\n class Bar : Foo() {\n override fun singleExpression() = super.singleExpression()\n }\n\nAfter the quick-fix is applied:\n\n\n class Bar : Foo() {\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RedundantOverride", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceArrayOfWithLiteral", + "shortDescription": { + "text": "'arrayOf' call can be replaced with array literal [...]" + }, + "fullDescription": { + "text": "Reports 'arrayOf' calls that can be replaced with array literals '[...]'. Examples: 'annotation class MyAnnotation(val strings: Array)\n\n @MyAnnotation(arrayOf(\"alpha\", \"beta\", \"omega\")) // replaceable 'arrayOf()'\n class MyClass' After the quick-fix is applied: 'annotation class MyAnnotation(val strings: Array)\n\n @MyAnnotation([\"alpha\", \"beta\", \"omega\"])\n class MyClass'", + "markdown": "Reports `arrayOf` calls that can be replaced with array literals `[...]`.\n\n**Examples:**\n\n annotation class MyAnnotation(val strings: Array)\n\n @MyAnnotation(arrayOf(\"alpha\", \"beta\", \"omega\")) // replaceable 'arrayOf()'\n class MyClass\n\nAfter the quick-fix is applied:\n\n annotation class MyAnnotation(val strings: Array)\n\n @MyAnnotation([\"alpha\", \"beta\", \"omega\"])\n class MyClass\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceArrayOfWithLiteral", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceRangeToWithUntil", + "shortDescription": { + "text": "'rangeTo' or the '..' call should be replaced with 'until'" + }, + "fullDescription": { + "text": "Reports calls to 'rangeTo' or the '..' operator instead of calls to 'until'. Using corresponding functions makes your code simpler. The quick-fix replaces 'rangeTo' or the '..' call with 'until'. Example: 'fun foo(a: Int) {\n for (i in 0..a - 1) {\n\n }\n }' After the quick-fix is applied: 'fun foo(a: Int) {\n for (i in 0 until a) {\n\n }\n }'", + "markdown": "Reports calls to `rangeTo` or the `..` operator instead of calls to `until`.\n\nUsing corresponding functions makes your code simpler.\n\nThe quick-fix replaces `rangeTo` or the `..` call with `until`.\n\n**Example:**\n\n\n fun foo(a: Int) {\n for (i in 0..a - 1) {\n\n }\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(a: Int) {\n for (i in 0 until a) {\n\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceRangeToWithUntil", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReplaceToWithInfixForm", + "shortDescription": { + "text": "'to' call should be replaced with infix form" + }, + "fullDescription": { + "text": "Reports 'to' function calls that can be replaced with the infix form. Using the infix form makes your code simpler. The quick-fix replaces 'to' with the infix form. Example: 'fun foo(a: Int, b: Int) {\n val pair = a.to(b)\n }' After the quick-fix is applied: 'fun foo(a: Int, b: Int) {\n val pair = a to b\n }'", + "markdown": "Reports `to` function calls that can be replaced with the infix form.\n\nUsing the infix form makes your code simpler.\n\nThe quick-fix replaces `to` with the infix form.\n\n**Example:**\n\n\n fun foo(a: Int, b: Int) {\n val pair = a.to(b)\n }\n\nAfter the quick-fix is applied:\n\n\n fun foo(a: Int, b: Int) {\n val pair = a to b\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ReplaceToWithInfixForm", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnusedEquals", + "shortDescription": { + "text": "Unused equals expression" + }, + "fullDescription": { + "text": "Reports unused 'equals'('==') expressions.", + "markdown": "Reports unused `equals`(`==`) expressions." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "UnusedEquals", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConstPropertyName", + "shortDescription": { + "text": "Const property naming convention" + }, + "fullDescription": { + "text": "Reports 'const' property names that do not follow the recommended naming conventions. Consistent naming allows for easier code reading and understanding. According to the Kotlin official style guide, 'const' properties should use uppercase underscore-separated names. Example: 'const val Planck: Double = 6.62607015E-34' The quick-fix renames the property: 'const val PLANCK: Double = 6.62607015E-34'", + "markdown": "Reports `const` property names that do not follow the recommended naming conventions.\n\n\nConsistent naming allows for easier code reading and understanding.\nAccording to the [Kotlin official style guide](https://kotlinlang.org/docs/coding-conventions.html#property-names),\n`const` properties should use uppercase underscore-separated names.\n\n**Example:**\n\n\n const val Planck: Double = 6.62607015E-34\n\nThe quick-fix renames the property:\n\n\n const val PLANCK: Double = 6.62607015E-34\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ConstPropertyName", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Naming conventions", + "index": 49, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantNullableReturnType", + "shortDescription": { + "text": "Redundant nullable return type" + }, + "fullDescription": { + "text": "Reports functions and variables with nullable return type which never return or become 'null'. Example: 'fun greeting(user: String): String? = \"Hello, $user!\"' After the quick-fix is applied: 'fun greeting(user: String): String = \"Hello, $user!\"'", + "markdown": "Reports functions and variables with nullable return type which never return or become `null`.\n\n**Example:**\n\n\n fun greeting(user: String): String? = \"Hello, $user!\"\n\nAfter the quick-fix is applied:\n\n\n fun greeting(user: String): String = \"Hello, $user!\"\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantNullableReturnType", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Redundant constructs", + "index": 5, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnclearPrecedenceOfBinaryExpression", + "shortDescription": { + "text": "Multiple operators with different precedence" + }, + "fullDescription": { + "text": "Reports binary expressions that consist of different operators without parentheses. Such expressions can be less readable due to different precedence rules of operators. Example: fun foo(b: Boolean?, i: Int?) {\n val x = b ?: i == null // evaluated as `(b ?: i) == null`\n val y = i ?: 0 + 1 // evaluated as `i ?: (0 + 1)`\n }", + "markdown": "Reports binary expressions that consist of different operators without parentheses.\n\nSuch expressions can be less readable due to different [precedence rules](https://kotlinlang.org/docs/reference/grammar.html#expressions) of operators.\n\nExample:\n\n```\n fun foo(b: Boolean?, i: Int?) {\n val x = b ?: i == null // evaluated as `(b ?: i) == null`\n val y = i ?: 0 + 1 // evaluated as `i ?: (0 + 1)`\n }\n```" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnclearPrecedenceOfBinaryExpression", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Style issues", + "index": 4, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UselessCallOnNotNull", + "shortDescription": { + "text": "Useless call on not-null type" + }, + "fullDescription": { + "text": "Reports calls on not-null receiver that make sense only for nullable receiver. Several functions from the standard library such as 'orEmpty()' or 'isNullOrEmpty' have sense only when they are called on receivers of nullable types. Otherwise, they can be omitted or simplified as the result will be the same. Remove redundant call and Change call to … quick-fixes can be used to amend the code automatically. Examples: 'fun test(s: String) {\n val x = s.orEmpty() // quick-fix simplifies to 's'\n val y = s.isNullOrEmpty() // quick-fix simplifies to 's.isEmpty()'\n }'", + "markdown": "Reports calls on not-null receiver that make sense only for nullable receiver.\n\nSeveral functions from the standard library such as `orEmpty()` or `isNullOrEmpty`\nhave sense only when they are called on receivers of nullable types. Otherwise, they can be omitted or simplified as the result will be the same.\n\n**Remove redundant call** and **Change call to ...** quick-fixes can be used to amend the code automatically.\n\nExamples:\n\n\n fun test(s: String) {\n val x = s.orEmpty() // quick-fix simplifies to 's'\n val y = s.isNullOrEmpty() // quick-fix simplifies to 's.isEmpty()'\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "UselessCallOnNotNull", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnusedLambdaExpressionBody", + "shortDescription": { + "text": "Unused return value of a function with lambda expression body" + }, + "fullDescription": { + "text": "Reports calls with an unused return value when the called function returns a lambda from an expression body. If there is '=' between function header and body block, code from the function will not be evaluated which can lead to incorrect behavior. Remove = token from function declaration can be used to amend the code automatically. Example: 'fun printHello() = { println(\"Hello\") }\n\n fun main() {\n printHello() // This function doesn't print anything\n }' After the quick-fix is applied: 'fun printHello() { println(\"Hello\") }\n\n fun main() {\n printHello()\n }'", + "markdown": "Reports calls with an unused return value when the called function returns a lambda from an expression body.\n\n\nIf there is `=` between function header and body block,\ncode from the function will not be evaluated which can lead to incorrect behavior.\n\n**Remove = token from function declaration** can be used to amend the code automatically.\n\nExample:\n\n\n fun printHello() = { println(\"Hello\") }\n\n fun main() {\n printHello() // This function doesn't print anything\n }\n\nAfter the quick-fix is applied:\n\n\n fun printHello() { println(\"Hello\") }\n\n fun main() {\n printHello()\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "UnusedLambdaExpressionBody", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EqualsOrHashCode", + "shortDescription": { + "text": "'equals()' and 'hashCode()' not paired" + }, + "fullDescription": { + "text": "Reports classes that override 'equals()' but do not override 'hashCode()', or vice versa. It also reports object declarations that override either 'equals()' or 'hashCode()'. This can lead to undesired behavior when a class is added to a 'Collection' Example: 'class C1 {\n override fun equals(other: Any?) = true\n }\n\n class C2 {\n override fun hashCode() = 0\n }\n\n object O1 {\n override fun equals(other: Any?) = true\n }\n\n object O2 {\n override fun hashCode() = 0\n }' The quick-fix overrides 'equals()' or 'hashCode()' for classes and deletes these methods for objects: 'class C1 {\n override fun equals(other: Any?) = true\n override fun hashCode(): Int {\n return javaClass.hashCode()\n }\n }\n\n class C2 {\n override fun hashCode() = 0\n override fun equals(other: Any?): Boolean {\n if (this === other) return true\n if (javaClass != other?.javaClass) return false\n return true\n }\n }\n\n object O1 {\n }\n\n object O2 {\n }'", + "markdown": "Reports classes that override `equals()` but do not override `hashCode()`, or vice versa. It also reports object declarations that override either `equals()` or `hashCode()`.\n\nThis can lead to undesired behavior when a class is added to a `Collection`\n\n**Example:**\n\n\n class C1 {\n override fun equals(other: Any?) = true\n }\n\n class C2 {\n override fun hashCode() = 0\n }\n\n object O1 {\n override fun equals(other: Any?) = true\n }\n\n object O2 {\n override fun hashCode() = 0\n }\n\nThe quick-fix overrides `equals()` or `hashCode()` for classes and deletes these methods for objects:\n\n\n class C1 {\n override fun equals(other: Any?) = true\n override fun hashCode(): Int {\n return javaClass.hashCode()\n }\n }\n\n class C2 {\n override fun hashCode() = 0\n override fun equals(other: Any?): Boolean {\n if (this === other) return true\n if (javaClass != other?.javaClass) return false\n return true\n }\n }\n\n object O1 {\n }\n\n object O2 {\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "EqualsOrHashCode", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Kotlin/Probable bugs", + "index": 24, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + } + ], + "language": "en-US", + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "isComprehensive": false + }, + { + "name": "org.intellij.groovy", + "version": "233.14714", + "rules": [ + { + "id": "GroovyListSetCanBeKeyedAccess", + "shortDescription": { + "text": "Call to List.set can be keyed access" + }, + "fullDescription": { + "text": "Reports calls to 'java.util.List.set()' methods. Such calls could be replaced by the shorter and clearer keyed access form. Example: 'def list = [\"foo\"]\nlist.set(0, \"bar\") // list.set(0, \"bar\") could be replaced with list[0] = \"bar\"'\n After the quick-fix is applied: 'def list = [\"foo\"]\nlist[0] = \"bar\"'", + "markdown": "Reports calls to `java.util.List.set()` methods. Such calls could be replaced by the shorter and clearer keyed access form.\n\n**Example:**\n\n\n def list = [\"foo\"]\n list.set(0, \"bar\") // list.set(0, \"bar\") could be replaced with list[0] = \"bar\"\n\nAfter the quick-fix is applied:\n\n\n def list = [\"foo\"]\n list[0] = \"bar\"\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyListSetCanBeKeyedAccess", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/GPath", + "index": 21, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyConstantNamingConvention", + "shortDescription": { + "text": "Constant naming convention" + }, + "fullDescription": { + "text": "Reports constant with names which don't match the specified convention. Constants are fields of immutable type declared with 'static' and 'final' modifiers. Reports constants whose names are either too short, too long, or do not follow the specified regular expression pattern. Configure the inspection: Use the Pattern field to specify 'java.util.regex.Pattern' which a constant name is expected to match. Use the Min length field to specify the minimum length of a constant name. Use the Max length field to specify the maximum length of a constant name.", + "markdown": "Reports constant with names which don't match the specified convention.\n\nConstants are fields of immutable type declared with `static` and `final` modifiers.\nReports constants whose names are either too short, too long, or do not follow the specified regular expression pattern.\n\nConfigure the inspection:\n\n* Use the **Pattern** field to specify `java.util.regex.Pattern` which a constant name is expected to match.\n* Use the **Min length** field to specify the minimum length of a constant name.\n* Use the **Max length** field to specify the maximum length of a constant name." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyConstantNamingConvention", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Naming conventions", + "index": 35, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyThreadStopSuspendResume", + "shortDescription": { + "text": "Call to Thread.stop(), Thread.suspend(), or Thread.resume()" + }, + "fullDescription": { + "text": "Reports calls to 'Thread.stop()','Thread.suspend()', or 'Thread.resume()'. These calls are inherently prone to data corruption and deadlock, and their use is strongly discouraged.", + "markdown": "Reports calls to `Thread.stop()`,`Thread.suspend()`, or `Thread.resume()`.\n\n\nThese calls are inherently prone to data corruption and deadlock, and their use is strongly\ndiscouraged." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyThreadStopSuspendResume", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Threading issues", + "index": 38, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyContinueOrBreakFromFinallyBlock", + "shortDescription": { + "text": "'continue' or 'break' from 'finally' block" + }, + "fullDescription": { + "text": "Reports 'break' and 'continue' statements inside of 'finally' blocks. While occasionally intended, such statements are very confusing, may mask thrown exceptions, and tremendously complicate debugging.", + "markdown": "Reports `break` and `continue` statements inside of `finally` blocks.\n\nWhile occasionally intended, such statements are very confusing, may mask thrown exceptions, and tremendously complicate debugging." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyContinueOrBreakFromFinallyBlock", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Error handling", + "index": 43, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyInArgumentCheck", + "shortDescription": { + "text": "Incompatible 'in' argument types" + }, + "fullDescription": { + "text": "Reports usages of membership operator 'in' with items and containers of incompatible types. Example: 'def list = [1, 2]\nif (\"foo\" in list) {} // list of Integers can't contain String'", + "markdown": "Reports usages of membership operator `in` with items and containers of incompatible types.\n\n**Example:**\n\n\n def list = [1, 2]\n if (\"foo\" in list) {} // list of Integers can't contain String\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyInArgumentCheck", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Probable bugs", + "index": 55, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyMapPutCanBeKeyedAccess", + "shortDescription": { + "text": "Call to Map.put can be keyed access" + }, + "fullDescription": { + "text": "Reports calls to 'java.util.Map.put()' methods. Such calls could be replaced by the shorter and clearer keyed access form. Example: 'def map = [\"foo\": \"bar\"]\nmap.put(\"foo\", \"baz\") // map.put(\"foo\", \"baz\") could be replaced with map[\"foo\"] = \"baz\"'\n After the quick-fix is applied: 'def map = [\"foo\": \"bar\"]\nmap[\"foo\"] = \"baz\"'", + "markdown": "Reports calls to `java.util.Map.put()` methods. Such calls could be replaced by the shorter and clearer keyed access form.\n\n**Example:**\n\n\n def map = [\"foo\": \"bar\"]\n map.put(\"foo\", \"baz\") // map.put(\"foo\", \"baz\") could be replaced with map[\"foo\"] = \"baz\"\n\nAfter the quick-fix is applied:\n\n\n def map = [\"foo\": \"bar\"]\n map[\"foo\"] = \"baz\"\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyMapPutCanBeKeyedAccess", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/GPath", + "index": 21, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyAssignmentToMethodParameter", + "shortDescription": { + "text": "Assignment to method parameter" + }, + "fullDescription": { + "text": "Reports assignment to method parameters. While occasionally intended, this construct can be extremely confusing, and is often the result of a typo. Example: 'def m(a, b, c) {\n a = [] // warning\n }'", + "markdown": "Reports assignment to method parameters.\n\nWhile occasionally intended, this construct can be extremely confusing, and is often the result of a typo.\n\n**Example:**\n\n\n def m(a, b, c) {\n a = [] // warning\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyAssignmentToMethodParameter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Assignment issues", + "index": 67, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyStaticMethodNamingConvention", + "shortDescription": { + "text": "Static method naming convention" + }, + "fullDescription": { + "text": "Reports static methods whose names are too short, too long, or do not follow the specified regular expression pattern. Use the fields provided below to specify minimum length, maximum length and regular expression expected for static method names. Regular expressions should be specified in the standard 'java.util.regex' format.", + "markdown": "Reports static methods whose names are too short, too long, or do not follow the specified regular expression pattern.\n\n\nUse the fields provided below to specify minimum length, maximum length and regular expression expected for static method names.\n\nRegular expressions should be specified in the standard `java.util.regex` format." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyStaticMethodNamingConvention", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Naming conventions", + "index": 35, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyTrivialIf", + "shortDescription": { + "text": "Redundant 'if' statement" + }, + "fullDescription": { + "text": "Reports 'if' statements which can be simplified to single assignment or 'return' statements. Example: 'if (foo())\n return true;\n else\n return false;' After the quick-fix is applied: 'return foo();'", + "markdown": "Reports `if` statements which can be simplified to single assignment or `return` statements.\n\n**Example:**\n\n\n if (foo())\n return true;\n else\n return false;\n\nAfter the quick-fix is applied:\n\n\n return foo();\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyTrivialIf", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Control flow issues", + "index": 72, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyPointlessArithmetic", + "shortDescription": { + "text": "Pointless arithmetic expression" + }, + "fullDescription": { + "text": "Reports pointless arithmetic expressions. Such expressions include adding or subtracting zero, multiplying by zero or one, division by one, and shift by zero. Such expressions may be the result of automated refactorings not completely followed through to completion, and in any case are unlikely to be what the developer intended to do. Example: 'a + 0' After the quick-fix is applied: 'a'", + "markdown": "Reports pointless arithmetic expressions.\n\n\nSuch expressions include adding or subtracting zero, multiplying by zero or one,\ndivision by one, and shift by zero. Such expressions may be the result of automated refactorings\nnot completely followed through to completion, and in any case are unlikely to be what the developer\nintended to do.\n\n**Example:**\n\n\n a + 0\n\nAfter the quick-fix is applied:\n\n\n a\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyPointlessArithmetic", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Potentially confusing code constructs", + "index": 74, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyAccessibility", + "shortDescription": { + "text": "Inaccessible element" + }, + "fullDescription": { + "text": "Reports references which exceed access rights. Access to private members breaks encapsulation.", + "markdown": "Reports references which exceed access rights.\n\nAccess to private members breaks encapsulation." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyAccessibility", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Probable bugs", + "index": 55, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyDoubleCheckedLocking", + "shortDescription": { + "text": "Double-checked locking" + }, + "fullDescription": { + "text": "Reports double-checked locking. Double-checked locking tries to initialize a field on demand and in a thread-safe manner, while avoiding the cost of synchronization. Unfortunately it is not thread-safe when used on a field that is not declared 'volatile'. When using Java 1.4 or earlier, double-checked locking doesn't work even with a 'volatile' field. Read the article linked above for a detailed explanation of the problem. Example: 'class Foo {\n private Helper helper = null\n\n Helper getHelper() {\n if (helper == null)\n synchronized(this) {\n if (helper == null) {\n helper = new Helper()\n }\n }\n }\n return helper;\n }\n }'", + "markdown": "Reports [double-checked locking](https://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html).\n\n\nDouble-checked locking tries to initialize a field on demand and in a thread-safe manner, while avoiding the cost of synchronization.\nUnfortunately it is not thread-safe when used on a field that is not declared `volatile`.\nWhen using Java 1.4 or earlier, double-checked locking doesn't work even with a `volatile` field.\nRead the article linked above for a detailed explanation of the problem.\n\n**Example:**\n\n\n class Foo {\n private Helper helper = null\n\n Helper getHelper() {\n if (helper == null)\n synchronized(this) {\n if (helper == null) {\n helper = new Helper()\n }\n }\n }\n return helper;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyDoubleCheckedLocking", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Threading issues", + "index": 38, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyFallthrough", + "shortDescription": { + "text": "Fallthrough in 'switch' statement" + }, + "fullDescription": { + "text": "Reports fallthrough in switch statements. While occasionally useful, fallthrough is often unintended, and may lead to surprising bugs. Example: 'switch(n) {\n case 1:\n print 1\n case 2: // \"case 1\" fallthrough to \"case 2\". Statements from \"case 2\" will be executed immediately after \"case 1\".\n print 2\n break\n default:\n print \"Default\"\n}'", + "markdown": "Reports *fallthrough* in switch statements. While occasionally useful, fallthrough is often unintended, and may lead to surprising bugs.\n\n**Example:**\n\n\n switch(n) {\n case 1:\n print 1\n case 2: // \"case 1\" fallthrough to \"case 2\". Statements from \"case 2\" will be executed immediately after \"case 1\".\n print 2\n break\n default:\n print \"Default\"\n }\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyFallthrough", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Control flow issues", + "index": 72, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyLocalVariableNamingConvention", + "shortDescription": { + "text": "Local variable naming convention" + }, + "fullDescription": { + "text": "Reports local variables whose names are too short, too long, or do not follow the specified regular expression pattern. Use the fields provided below to specify minimum length, maximum length and regular expression expected for local variables names. Regular expressions should be specified in the standard 'java.util.regex' format.", + "markdown": "Reports local variables whose names are too short, too long, or do not follow the specified regular expression pattern.\n\n\nUse the fields provided below to specify minimum length, maximum length and regular expression expected for local variables names.\n\nRegular expressions should be specified in the standard `java.util.regex` format." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyLocalVariableNamingConvention", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Naming conventions", + "index": 35, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyIfStatementWithIdenticalBranches", + "shortDescription": { + "text": "If statement with identical branches" + }, + "fullDescription": { + "text": "Reports 'if' statements with identical \"then\" and 'else' branches. Such statements are almost certainly programmer error. Example: 'if (condition) {\n print \"foo\"\n} else {\n print \"foo\"\n}'\n After the quick-fix is applied: 'print \"foo\"'", + "markdown": "Reports `if` statements with identical \"then\" and `else` branches. Such statements are almost certainly programmer error.\n\n**Example:**\n\n\n if (condition) {\n print \"foo\"\n } else {\n print \"foo\"\n }\n\nAfter the quick-fix is applied:\n\n\n print \"foo\"\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyIfStatementWithIdenticalBranches", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Control flow issues", + "index": 72, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyUncheckedAssignmentOfMemberOfRawType", + "shortDescription": { + "text": "Unchecked assignment from members of raw type" + }, + "fullDescription": { + "text": "Reports unchecked assignments from members of raw type. Example: 'List list = new ArrayList()\n List<String> a = list.get(0)'", + "markdown": "Reports unchecked assignments from members of raw type.\n\n**Example:**\n\n\n List list = new ArrayList()\n List<String> a = list.get(0)\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyUncheckedAssignmentOfMemberOfRawType", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Assignment issues", + "index": 67, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyNonShortCircuitBoolean", + "shortDescription": { + "text": "Non short-circuit boolean" + }, + "fullDescription": { + "text": "Reports the non-short-circuit forms of boolean operators 'and' and 'or' ( '&' and '|' ). The non-short-circuit versions are occasionally useful, but their presence is often due to typos of the short-circuit forms ( '&&' and '||' ), and may lead to subtle bugs. Example: 'if (a & b) {}' After the quick-fix is applied: 'if (a && b) {}'", + "markdown": "Reports the non-short-circuit forms of boolean operators 'and' and 'or' ( `&` and `|` ).\n\n\nThe non-short-circuit versions are occasionally useful, but\ntheir presence is often due to typos of the short-circuit forms ( `&&`\nand `||` ), and may lead to subtle bugs.\n\n**Example:**\n\n\n if (a & b) {}\n\nAfter the quick-fix is applied:\n\n\n if (a && b) {}\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyNonShortCircuitBoolean", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Probable bugs", + "index": 55, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GrMethodMayBeStatic", + "shortDescription": { + "text": "Method can be made 'static'" + }, + "fullDescription": { + "text": "Reports methods which may safely be made 'static'. A method may be 'static' if it is not 'synchronized', it does not reference any of its class' instance methods and instance fields, and it is not overridden in a subclass.", + "markdown": "Reports methods which may safely be made `static`.\n\n\nA method may be `static` if it is not `synchronized`,\nit does not reference any of its class' instance methods and instance fields,\nand it is not overridden in a subclass." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GrMethodMayBeStatic", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Other", + "index": 81, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyDivideByZero", + "shortDescription": { + "text": "Division by zero" + }, + "fullDescription": { + "text": "Reports divisions by zero or remainders by zero. Example: 'def a = 42\n a / 0 // warning\n a % 0.0 // warning'", + "markdown": "Reports divisions by zero or remainders by zero.\n\n**Example:**\n\n\n def a = 42\n a / 0 // warning\n a % 0.0 // warning\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyDivideByZero", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Probable bugs", + "index": 55, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JavaStylePropertiesInvocation", + "shortDescription": { + "text": "Java-style property access" + }, + "fullDescription": { + "text": "Reports properties accessed via method calls. Example: 'class Foo {\n int foo\n }\n\n def bar = new Foo()\n print(bar.getFoo())' After the quick-fix is applied: 'class Foo {\n int foo\n }\n\n def bar = new Foo()\n print(bar.foo)'", + "markdown": "Reports properties accessed via method calls.\n\n**Example:**\n\n\n class Foo {\n int foo\n }\n\n def bar = new Foo()\n print(bar.getFoo())\n\nAfter the quick-fix is applied:\n\n\n class Foo {\n int foo\n }\n\n def bar = new Foo()\n print(bar.foo)\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "JavaStylePropertiesInvocation", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Style", + "index": 84, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyTrivialConditional", + "shortDescription": { + "text": "Redundant conditional expression" + }, + "fullDescription": { + "text": "Reports ternary conditional operators of the form 'x ? true : false' or similar, which can be trivially simplified. Example: 'foo() ? true : false' After the quick-fix is applied: 'foo()'", + "markdown": "Reports ternary conditional operators of the form `x ? true : false` or similar, which can be trivially simplified.\n\n**Example:**\n\n\n foo() ? true : false\n\nAfter the quick-fix is applied:\n\n\n foo()\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyTrivialConditional", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Control flow issues", + "index": 72, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GrUnnecessarySemicolon", + "shortDescription": { + "text": "Unnecessary semicolon" + }, + "fullDescription": { + "text": "Reports unnecessary semicolons. Example: 'print 2; print 3 // semicolon is required\n print 2; // semicolon is unnecessary'", + "markdown": "Reports unnecessary semicolons.\n\n**Example:**\n\n\n print 2; print 3 // semicolon is required\n print 2; // semicolon is unnecessary\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GrUnnecessarySemicolon", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Style", + "index": 84, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GrFinalVariableAccess", + "shortDescription": { + "text": "Final variable access" + }, + "fullDescription": { + "text": "Reports uninitialized final fields, invalid assignments to final variables, and parameters and fields.", + "markdown": "Reports uninitialized final fields, invalid assignments to final variables, and parameters and fields." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GrFinalVariableAccess", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Control flow issues", + "index": 72, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ChangeToMethod", + "shortDescription": { + "text": "Operator invocation can be replaced with method call" + }, + "fullDescription": { + "text": "Reports operator invocations that can be replaced with method calls. Example: 'a + b' After the quick-fix is applied: 'a.plus(b)'", + "markdown": "Reports operator invocations that can be replaced with method calls.\n\n**Example:**\n\n\n a + b\n\nAfter the quick-fix is applied:\n\n\n a.plus(b)\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ChangeToMethod", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Style", + "index": 84, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnnecessaryQualifiedReference", + "shortDescription": { + "text": "Unnecessary qualified reference" + }, + "fullDescription": { + "text": "Reports fully qualified references, which can be replaced with import. Example: 'def swingBuilder = new groovy.swing.SwingBuilder()' After the quick-fix is applied: 'import groovy.swing.SwingBuilder\n \n def swingBuilder = new SwingBuilder()'", + "markdown": "Reports fully qualified references, which can be replaced with import.\n\n**Example:**\n\n\n def swingBuilder = new groovy.swing.SwingBuilder()\n\nAfter the quick-fix is applied:\n\n\n import groovy.swing.SwingBuilder\n \n def swingBuilder = new SwingBuilder()\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnnecessaryQualifiedReference", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Potentially confusing code constructs", + "index": 74, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyBreak", + "shortDescription": { + "text": "'break' statement" + }, + "fullDescription": { + "text": "Reports 'break' statements outside of 'switch' statements.", + "markdown": "Reports `break` statements outside of `switch` statements." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyBreak", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Control flow issues", + "index": 72, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DelegatesTo", + "shortDescription": { + "text": "@DelegatesTo" + }, + "fullDescription": { + "text": "Reports unused '@DelegatesTo.Target' annotations and unresolved '@DelegatedTo.target' annotation attribute values. Example: '// unused target 't1' and unresolved target 't2'\n def m(\n @DelegatesTo.Target('t1') target,\n @DelegatesTo(target = 't2') Closure c\n ) {}'", + "markdown": "Reports unused `@DelegatesTo.Target` annotations and unresolved `@DelegatedTo.target` annotation attribute values.\n\n**Example:**\n\n\n // unused target 't1' and unresolved target 't2'\n def m(\n @DelegatesTo.Target('t1') target,\n @DelegatesTo(target = 't2') Closure c\n ) {}\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "DelegatesTo", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Annotations", + "index": 91, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyConstantConditional", + "shortDescription": { + "text": "Constant conditional expression" + }, + "fullDescription": { + "text": "Reports conditional expressions with boolean constant as a condition. Example: 'true ? result1 : result2\n false ? result1 : result2'", + "markdown": "Reports conditional expressions with boolean constant as a condition.\n\n**Example:**\n\n\n true ? result1 : result2\n false ? result1 : result2\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyConstantConditional", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Control flow issues", + "index": 72, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyOverlyComplexArithmeticExpression", + "shortDescription": { + "text": "Overly complex arithmetic expression" + }, + "fullDescription": { + "text": "Reports arithmetic expressions with too many terms. Such expressions may be confusing and bug-prone. Use the Maximum number of terms field to specify the maximum number of terms allowed in an arithmetic expression.", + "markdown": "Reports arithmetic expressions with too many terms.\n\n\nSuch expressions may be confusing and bug-prone.\n\n\nUse the **Maximum number of terms** field to specify the maximum number of terms allowed in an arithmetic expression." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyOverlyComplexArithmeticExpression", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Potentially confusing code constructs", + "index": 74, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GrSwitchExhaustivenessCheck", + "shortDescription": { + "text": "Exhaustiveness check for switch expressions" + }, + "fullDescription": { + "text": "Reports switch expressions that do not cover all possible outcomes of the matched expression. Groovy does not require that switch expression must be exhaustive. It acts as if an implicit 'default -> null' branch is inserted. It may cause unexpected nulls if a developer forgets to insert necessary 'case' branches. Example: 'enum A { X, Y }\n\n def foo(A a) {\n def x = switch (a) { // reports switch\n case A.X -> ...\n }\n }'", + "markdown": "Reports switch expressions that do not cover all possible outcomes of the matched expression.\n\nGroovy does not require that switch expression must be exhaustive. It acts as if an implicit `default -> null` branch is inserted.\nIt may cause unexpected nulls if a developer forgets to insert necessary `case` branches.\n\n**Example:**\n\n\n enum A { X, Y }\n\n def foo(A a) {\n def x = switch (a) { // reports switch\n case A.X -> ...\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "GrSwitchExhaustivenessCheck", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Probable bugs", + "index": 55, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyThrowFromFinallyBlock", + "shortDescription": { + "text": "'throw' inside 'finally' block" + }, + "fullDescription": { + "text": "Reports 'throw' statements inside of 'finally' blocks. While occasionally intended, such 'throw' statements may mask exceptions thrown and tremendously complicate debugging.", + "markdown": "Reports `throw` statements inside of `finally` blocks.\n\n\nWhile occasionally intended, such `throw` statements may mask exceptions thrown and\ntremendously complicate debugging." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyThrowFromFinallyBlock", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Error handling", + "index": 43, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyAssignabilityCheck", + "shortDescription": { + "text": "Incompatible type assignments" + }, + "fullDescription": { + "text": "Reports assignments with incompatible types. Such assignments might result in various runtime exceptions. Example: 'class A {}\n class B {}\n\n // incompatible assignment\n A a = new B()'", + "markdown": "Reports assignments with incompatible types.\n\nSuch assignments might result in various runtime exceptions.\n\n**Example:**\n\n\n class A {}\n class B {}\n\n // incompatible assignment\n A a = new B()\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyAssignabilityCheck", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Assignment issues", + "index": 67, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ChangeToOperator", + "shortDescription": { + "text": "Method call can be replaced with operator invocation" + }, + "fullDescription": { + "text": "Reports method calls that can be replaced with operator invocations. Example: 'a.plus(b)' After the quick-fix is applied: 'a + b'", + "markdown": "Reports method calls that can be replaced with operator invocations.\n\n**Example:**\n\n\n a.plus(b)\n\nAfter the quick-fix is applied:\n\n\n a + b\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ChangeToOperator", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Style", + "index": 84, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyMapGetCanBeKeyedAccess", + "shortDescription": { + "text": "Call to Map.get can be keyed access" + }, + "fullDescription": { + "text": "Reports calls to 'java.util.Map.get()' methods. Such calls could be replaced by the shorter and clearer keyed access form. Example: 'def map = [\"foo\": \"bar\"]\ndef str = map.get(\"foo\") // map.get(\"foo\") could be replaced with map[\"foo\"]'\n After the quick-fix is applied: 'def map = [\"foo\": \"bar\"]\ndef str = map[\"foo\"]'", + "markdown": "Reports calls to `java.util.Map.get()` methods. Such calls could be replaced by the shorter and clearer keyed access form.\n\n**Example:**\n\n\n def map = [\"foo\": \"bar\"]\n def str = map.get(\"foo\") // map.get(\"foo\") could be replaced with map[\"foo\"]\n\nAfter the quick-fix is applied:\n\n\n def map = [\"foo\": \"bar\"]\n def str = map[\"foo\"]\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyMapGetCanBeKeyedAccess", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/GPath", + "index": 21, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyResultOfAssignmentUsed", + "shortDescription": { + "text": "Result of assignment used" + }, + "fullDescription": { + "text": "Reports assignment expressions nested inside other expressions to use the assigned value immediately. Such expressions may be confusing and violating the general design principle that a given construct should do precisely one thing.", + "markdown": "Reports assignment expressions nested inside other expressions to use the assigned value immediately.\n\n\nSuch expressions may be confusing and violating the general design principle that a\ngiven construct should do precisely one thing." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyResultOfAssignmentUsed", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Assignment issues", + "index": 67, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyUntypedAccess", + "shortDescription": { + "text": "Untyped reference expression" + }, + "fullDescription": { + "text": "Reports reference expressions whose type can't be determined.", + "markdown": "Reports reference expressions whose type can't be determined." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyUntypedAccess", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Probable bugs", + "index": 55, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyResultOfObjectAllocationIgnored", + "shortDescription": { + "text": "Result of object allocation ignored" + }, + "fullDescription": { + "text": "Reports object allocation where the result of this operation is ignored. Such allocation expressions are legal Groovy, but are usually either inadvertent, or evidence of a complicated object initialization strategy.", + "markdown": "Reports object allocation where the result of this operation is ignored.\n\n\nSuch allocation expressions are legal Groovy, but are usually either inadvertent, or\nevidence of a complicated object initialization strategy." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyResultOfObjectAllocationIgnored", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Probable bugs", + "index": 55, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyNestedAssignment", + "shortDescription": { + "text": "Nested assignment" + }, + "fullDescription": { + "text": "Reports assignment expressions nested inside other expressions. While admirably terse, such expressions may be confusing, and violate the general design principle that a given construct should do precisely one thing. Example: 'a = b = 1'", + "markdown": "Reports assignment expressions nested inside other expressions. While admirably terse, such expressions may be confusing, and violate the general design principle that a given construct should do precisely one thing.\n\n**Example:**\n\n\n a = b = 1\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyNestedAssignment", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Assignment issues", + "index": 67, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyDocCheck", + "shortDescription": { + "text": "Unresolved GroovyDoc reference" + }, + "fullDescription": { + "text": "Reports unresolved references inside GroovyDoc comments.", + "markdown": "Reports unresolved references inside GroovyDoc comments." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "GroovyDocCheck", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Probable bugs", + "index": 55, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyUnusedCatchParameter", + "shortDescription": { + "text": "Unused 'catch' parameter" + }, + "fullDescription": { + "text": "Reports catch parameters that are unused in their corresponding blocks. This inspection will not report any catch parameters named \"ignore\" or \"ignored\". Example: 'try {\n def arr = new int[3]\n arr[5] = 5\n } catch(Exception ex) {\n println('Catching the exception')\n }' Here the parameter ex is never used in catch block. After the quick-fix is applied: 'try {\n def arr = new int[3]\n arr[5] = 5\n } catch(Exception ignored) {\n println('Catching the exception')\n }'", + "markdown": "Reports **catch** parameters that are unused in their\ncorresponding blocks. This inspection will not report any **catch** parameters\nnamed \"ignore\" or \"ignored\".\n\n**Example:**\n\n\n try {\n def arr = new int[3]\n arr[5] = 5\n } catch(Exception ex) {\n println('Catching the exception')\n }\n\nHere the parameter **ex** is never used in **catch** block.\n\nAfter the quick-fix is applied:\n\n\n try {\n def arr = new int[3]\n arr[5] = 5\n } catch(Exception ignored) {\n println('Catching the exception')\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyUnusedCatchParameter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Error handling", + "index": 43, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyInstanceMethodNamingConvention", + "shortDescription": { + "text": "Instance method naming convention" + }, + "fullDescription": { + "text": "Reports instance methods whose names are too short, too long, or do not follow the specified regular expression pattern. Instance methods that override library methods are ignored by this inspection. Use the fields provided below to specify minimum length, maximum length and regular expression expected for instance method names. Regular expressions should be specified in the standard 'java.util.regex' format.", + "markdown": "Reports instance methods whose names are too short, too long, or do not follow the specified regular expression pattern. Instance methods that override library methods are ignored by this inspection.\n\n\nUse the fields provided below to specify minimum length, maximum length and regular expression expected for instance method names.\n\nRegular expressions should be specified in the standard `java.util.regex` format." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyInstanceMethodNamingConvention", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Naming conventions", + "index": 35, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovySynchronizedMethod", + "shortDescription": { + "text": "Synchronized method" + }, + "fullDescription": { + "text": "Reports the 'synchronized' modifier on methods. Some coding standards prohibit the use of the 'synchronized' modifier, in favor of 'synchronized' statements.", + "markdown": "Reports the `synchronized` modifier on methods.\n\n\nSome coding standards\nprohibit the use of the `synchronized` modifier, in favor of `synchronized` statements." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovySynchronizedMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Threading issues", + "index": 38, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyUnconditionalWait", + "shortDescription": { + "text": "Unconditional 'wait' call" + }, + "fullDescription": { + "text": "Reports wait() being called unconditionally within a synchronized context. Normally, wait() is used to block a thread until some condition is true. If wait() is called unconditionally, that often indicates that the condition was checked before a lock was acquired. In that case, a data race may occur, with the condition becoming true between the time it was checked and the time the lock was acquired. While constructs found by this inspection are not necessarily incorrect, they are certainly worth examining.", + "markdown": "Reports **wait()**\nbeing called unconditionally within a synchronized context.\nNormally, **wait()** is\nused to block a thread until some condition is true. If **wait()**\nis called unconditionally, that often indicates that the condition was checked before a lock was\nacquired. In that case, a data race may occur, with the condition becoming true between the time\nit was checked and the time the lock was acquired. While constructs found by this inspection\nare not necessarily incorrect, they are certainly worth examining." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyUnconditionalWait", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Threading issues", + "index": 38, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyContinue", + "shortDescription": { + "text": "'continue' statement" + }, + "fullDescription": { + "text": "Reports 'continue' statements.", + "markdown": "Reports `continue` statements." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyContinue", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Control flow issues", + "index": 72, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyUnsynchronizedMethodOverridesSynchronizedMethod", + "shortDescription": { + "text": "Unsynchronized method overrides synchronized method" + }, + "fullDescription": { + "text": "Reports non-synchronized methods overriding synchronized methods. Example: 'abstract class Base {\n synchronized void foo() {\n // ...\n }\n }\n class Derived extends Base {\n @Override\n void foo() {\n super.foo()\n // ...\n }\n }' Here the non-synchronized method 'foo()' in class 'Bar' overrides synchronized method.", + "markdown": "Reports non-**synchronized** methods overriding **synchronized** methods.\n\n**Example:**\n\n\n abstract class Base {\n synchronized void foo() {\n // ...\n }\n }\n class Derived extends Base {\n @Override\n void foo() {\n super.foo()\n // ...\n }\n }\n\nHere the non-synchronized method `foo()` in class `Bar` overrides synchronized method." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyUnsynchronizedMethodOverridesSynchronizedMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Threading issues", + "index": 38, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyNegatedIf", + "shortDescription": { + "text": "Negated if condition expression" + }, + "fullDescription": { + "text": "Reports 'if' statements which contain 'else' branches and whose conditions are negated. Flipping the order of the 'if' and 'else' branches will usually increase the clarity of such statements. Example: 'if (!condition) {\n return \"1\"\n} else {\n return \"2\"\n}'", + "markdown": "Reports `if` statements which contain `else` branches and whose conditions are negated. Flipping the order of the `if` and `else` branches will usually increase the clarity of such statements.\n\n**Example:**\n\n\n if (!condition) {\n return \"1\"\n } else {\n return \"2\"\n }\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyNegatedIf", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Potentially confusing code constructs", + "index": 74, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NewGroovyClassNamingConvention", + "shortDescription": { + "text": "Class naming convention" + }, + "fullDescription": { + "text": "Reports classes whose names are too short, too long, or do not follow the specified regular expression pattern. For each class type, specify the minimum length, maximum length, and the regular expression expected for class names using the provided input fields. Regular expressions should be specified in the standard 'java.util.regex' format.", + "markdown": "Reports classes whose names are too short, too long, or do not follow\nthe specified regular expression pattern.\n\nFor each class type, specify the minimum length, maximum length, and the regular expression expected for class names using the\nprovided input fields.\n\nRegular expressions should be specified in the standard `java.util.regex` format." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NewGroovyClassNamingConvention", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Naming conventions", + "index": 35, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClashingGetters", + "shortDescription": { + "text": "Clashing getters" + }, + "fullDescription": { + "text": "Reports boolean methods which can be accessed via the same property name. The result of accessing such property might be unexpected. Example: 'class X {\n boolean isFoo() { true }\n boolean getFoo() { false }\n }\n\n // getFoo() will be called\n new X().foo'", + "markdown": "Reports boolean methods which can be accessed via the same property name.\n\nThe result of accessing such property might be unexpected.\n\n**Example:**\n\n\n class X {\n boolean isFoo() { true }\n boolean getFoo() { false }\n }\n\n // getFoo() will be called\n new X().foo\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClashingGetters", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Potentially confusing code constructs", + "index": 74, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyDuplicateSwitchBranch", + "shortDescription": { + "text": "Duplicate switch case" + }, + "fullDescription": { + "text": "Reports duplicated expressions in 'case' labels for 'switch' statements. Example: 'switch (n) {\n case 1: //duplicate\n break\n case 1: //duplicate\n System.out.println(\"2\")\n break\n default:\n System.out.println(\"default\");\n}'", + "markdown": "Reports duplicated expressions in `case` labels for `switch` statements.\n\n**Example:**\n\n\n switch (n) {\n case 1: //duplicate\n break\n case 1: //duplicate\n System.out.println(\"2\")\n break\n default:\n System.out.println(\"default\");\n }\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyDuplicateSwitchBranch", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Validity issues", + "index": 101, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovySwitchStatementWithNoDefault", + "shortDescription": { + "text": "Switch statement with no default case" + }, + "fullDescription": { + "text": "Reports 'switch' statements that do not contain 'default' labels. Some coding practices may insist on adding this label to all 'switch' statements.", + "markdown": "Reports `switch` statements that do not contain `default` labels.\n\n\nSome coding practices may insist on adding this label to all `switch` statements." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovySwitchStatementWithNoDefault", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Control flow issues", + "index": 72, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyListGetCanBeKeyedAccess", + "shortDescription": { + "text": "Call to List.get can be keyed access" + }, + "fullDescription": { + "text": "Reports calls to 'java.util.List.get()' methods. Such calls could be replaced by the shorter and clearer keyed access form. Example: 'def list = [\"foo\"]\ndef str = list.get(0) // list.get(0) could be replaced with list[0]'\n After the quick-fix is applied: 'def list = [\"foo\"]\ndef str = list[0]'", + "markdown": "Reports calls to `java.util.List.get()` methods. Such calls could be replaced by the shorter and clearer keyed access form.\n\n**Example:**\n\n\n def list = [\"foo\"]\n def str = list.get(0) // list.get(0) could be replaced with list[0]\n\nAfter the quick-fix is applied:\n\n\n def list = [\"foo\"]\n def str = list[0]\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyListGetCanBeKeyedAccess", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/GPath", + "index": 21, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovySynchronizationOnThis", + "shortDescription": { + "text": "Synchronization on 'this'" + }, + "fullDescription": { + "text": "Reports synchronization which uses 'this' as its lock expression. Constructs reported include 'synchronized' blocks which lock 'this', and calls to 'wait()' 'notify()' or 'notifyAll()' which target 'wait()'. Such constructs, like synchronized methods, make it hard to track just who is locking on a given object, and make possible \"denial of service\" attacks on objects. As an alternative, consider locking on a private instance variable, access to which can be completely controlled.", + "markdown": "Reports synchronization which uses `this` as its lock expression.\n\n\nConstructs reported include `synchronized`\nblocks which lock `this`, and calls to `wait()`\n`notify()` or `notifyAll()` which target `wait()`.\nSuch constructs, like synchronized methods, make it hard to track just who is locking on a given\nobject, and make possible \"denial of service\" attacks on objects. As an alternative, consider\nlocking on a private instance variable, access to which can be completely controlled." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovySynchronizationOnThis", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Threading issues", + "index": 38, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyNestedSynchronizedStatement", + "shortDescription": { + "text": "Nested 'synchronized' statement" + }, + "fullDescription": { + "text": "Reports nested 'synchronized' statements. Nested 'synchronized' statements are either redundant (if the lock objects are identical) or prone to deadlock.", + "markdown": "Reports nested `synchronized` statements.\n\n\nNested `synchronized` statements\nare either redundant (if the lock objects are identical) or prone to deadlock." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyNestedSynchronizedStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Threading issues", + "index": 38, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyGStringKey", + "shortDescription": { + "text": "GString map key" + }, + "fullDescription": { + "text": "Reports statements which use a 'groovy.lang.GString' object as a key to map. In general 'GString' objects are mutable and probably should not be used as keys. Also, a 'GString' entry cannot be accessed with a 'java.lang.String' object with same value. Example: 'def map = [:]\ndef key = 'foo'\nmap << [\"${key}\": 'bar']\nassert map[key] == null // confusing 'true' result of comparison'\n New in 2017.1", + "markdown": "Reports statements which use a `groovy.lang.GString` object as a key to map. In general `GString` objects are mutable and probably should not be used as keys. Also, a `GString` entry cannot be accessed with a `java.lang.String` object with same value.\n\n**Example:**\n\n\n def map = [:]\n def key = 'foo'\n map << [\"${key}\": 'bar']\n assert map[key] == null // confusing 'true' result of comparison\n\nNew in 2017.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyGStringKey", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Potentially confusing code constructs", + "index": 74, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NewInstanceOfSingleton", + "shortDescription": { + "text": "New instance of class annotated with @groovy.lang.Singleton" + }, + "fullDescription": { + "text": "Reports new instance creation of classes annotated with '@groovy.lang.Singleton'. Such constructions can lead to runtime exception Can't instantiate singleton. Example: '@Singleton\n class Foo{\n }\n \n Foo foo = new Foo()' After the quick-fix is applied: '@Singleton\n class Foo{\n }\n \n Foo foo = Foo.instance'", + "markdown": "Reports new instance creation of classes annotated with `@groovy.lang.Singleton`.\nSuch constructions can lead to runtime exception **Can't instantiate singleton**.\n\n**Example:**\n\n\n @Singleton\n class Foo{\n }\n \n Foo foo = new Foo()\n\nAfter the quick-fix is applied:\n\n\n @Singleton\n class Foo{\n }\n \n Foo foo = Foo.instance\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NewInstanceOfSingleton", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Potentially confusing code constructs", + "index": 74, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyVariableCanBeFinal", + "shortDescription": { + "text": "Variable can be final" + }, + "fullDescription": { + "text": "Reports parameters or local variables that may have a final modifier added. Example: 'def list = [1,2,3]\n return list' After the quick-fix is applied: 'final def list = [1,2,3]\n return list' For more information, see the same inspection in Java.", + "markdown": "Reports parameters or local variables that may have a final modifier added.\n\n**Example:**\n\n\n def list = [1,2,3]\n return list\n\nAfter the quick-fix is applied:\n\n\n final def list = [1,2,3]\n return list\n\nFor more information, see the same inspection in Java." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyVariableCanBeFinal", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Data flow", + "index": 106, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyWaitCallNotInLoop", + "shortDescription": { + "text": "'wait()' not in loop" + }, + "fullDescription": { + "text": "Reports calls to 'wait()' not made inside a loop. 'wait()' is normally used to suspend a thread until a condition is true, and that condition should be checked after the 'wait()' returns. A loop is the clearest way to achieve this.", + "markdown": "Reports calls to `wait()` not made inside a loop.\n\n`wait()` is normally used to suspend a thread until a condition is true, and that condition should be checked after the `wait()`\nreturns. A loop is the clearest way to achieve this." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyWaitCallNotInLoop", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Threading issues", + "index": 38, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyUnnecessaryReturn", + "shortDescription": { + "text": "Unnecessary 'return' statement" + }, + "fullDescription": { + "text": "Reports 'return' statements at the end of constructors and methods returning 'void'. These are unnecessary and may be safely removed. Example: 'void foo (String s){\n print(s)\n return\n }' After the quick-fix is applied: 'void foo (String s){\n print(s)\n }' For more information, see the same inspection in Java.", + "markdown": "Reports `return` statements at the end of constructors and methods returning\n`void`. These are unnecessary and may be safely removed.\n\n**Example:**\n\n\n void foo (String s){\n print(s)\n return\n }\n\nAfter the quick-fix is applied:\n\n\n void foo (String s){\n print(s)\n }\n\nFor more information, see the same inspection in Java." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyUnnecessaryReturn", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Control flow issues", + "index": 72, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyMethodParameterCount", + "shortDescription": { + "text": "Method with too many parameters" + }, + "fullDescription": { + "text": "Reports methods with too many parameters. Method with too many parameters is a good sign that refactoring is necessary. Methods whose signatures are inherited from library classes are ignored by this inspection. Use the Maximum number of parameters: field to specify the maximum acceptable number of parameters a method might have.", + "markdown": "Reports methods with too many parameters. Method with too many parameters is a good sign that refactoring is necessary. Methods whose signatures are inherited from library classes are ignored by this inspection.\n\n\nUse the **Maximum number of parameters:** field to specify the maximum acceptable number of parameters a method might have." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyMethodParameterCount", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Method metrics", + "index": 107, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GrReassignedInClosureLocalVar", + "shortDescription": { + "text": "Local variable is reassigned in closure or anonymous class" + }, + "fullDescription": { + "text": "Reports local variables assigned to expression with different type inside of closure or anonymous class. Example: 'int sum = 0\n [1, 2, 3].each { sum += 'as' }\n println(sum)' As a result, the 'integer' variable sum is reassigned to a 'String' expression.", + "markdown": "Reports local variables assigned to expression with different type inside of closure or anonymous class.\n\n**Example:**\n\n\n int sum = 0\n [1, 2, 3].each { sum += 'as' }\n println(sum)\n\nAs a result, the `integer` variable **sum** is reassigned to a `String` expression." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GrReassignedInClosureLocalVar", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Potentially confusing code constructs", + "index": 74, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyConditional", + "shortDescription": { + "text": "Ternary expression" + }, + "fullDescription": { + "text": "Reports ternary expressions. Some coding standards prohibit the use of the condition operator in favor of 'if' statements.", + "markdown": "Reports ternary expressions.\n\nSome coding standards prohibit the use of the condition operator in favor of `if` statements." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyConditional", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Style", + "index": 84, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyAccessToStaticFieldLockedOnInstance", + "shortDescription": { + "text": "Access to static field locked on instance data" + }, + "fullDescription": { + "text": "Reports accesses to a non-constant static field which is locked on either 'this' or an instance field of 'this'. Locking a static field on instance data does not prevent the field from being modified by other instances, and thus may result in surprising race conditions. Example: 'static String s;\n def foo() {\n synchronized (this) {\n System.out.println(s); // warning\n }\n }'", + "markdown": "Reports accesses to a non-constant static field which is locked on either `this` or an instance field of `this`.\n\n\nLocking a static field on instance data does not prevent the field from being\nmodified by other instances, and thus may result in surprising race conditions.\n\n**Example:**\n\n\n static String s;\n def foo() {\n synchronized (this) {\n System.out.println(s); // warning\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyAccessToStaticFieldLockedOnInstance", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Threading issues", + "index": 38, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyNestedConditional", + "shortDescription": { + "text": "Nested conditional expression" + }, + "fullDescription": { + "text": "Reports ternary conditional expressions that are nested inside other conditional expressions. Such nested conditionals may be very confusing. \"Elvis\" expressions are counted as conditionals for purpose of this inspection. Example: 'return (condition ? \"result\" : null) ?: \"fail\"'", + "markdown": "Reports ternary conditional expressions that are nested inside other conditional expressions. Such nested conditionals may be very confusing. \"Elvis\" expressions are counted as conditionals for purpose of this inspection.\n\n**Example:**\n\n\n return (condition ? \"result\" : null) ?: \"fail\"\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyNestedConditional", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Potentially confusing code constructs", + "index": 74, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyEmptyStatementBody", + "shortDescription": { + "text": "Statement with empty body" + }, + "fullDescription": { + "text": "Reports 'if', 'while', 'do' or 'for' statements with empty bodies. While occasionally intended, this construction is confusing, and often the result of a typo. Example: 'if (condition) {}\nwhile(true){}'", + "markdown": "Reports `if`, `while`, `do` or `for` statements with empty bodies. While occasionally intended, this construction is confusing, and often the result of a typo.\n\n**Example:**\n\n\n if (condition) {}\n while(true){}\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyEmptyStatementBody", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Potentially confusing code constructs", + "index": 74, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyLabeledStatement", + "shortDescription": { + "text": "Labeled statement inspection" + }, + "fullDescription": { + "text": "Reports labels already used in parent workflow. Example: 'def list = [\"foo\"]\ncycle:\nfor (element in list) {\n cycle: // confusing label repeat\n element.chars().forEach {\n }\n}'", + "markdown": "Reports labels already used in parent workflow.\n\n**Example:**\n\n\n def list = [\"foo\"]\n cycle:\n for (element in list) {\n cycle: // confusing label repeat\n element.chars().forEach {\n }\n }\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyLabeledStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Probable bugs", + "index": 55, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyAssignmentToForLoopParameter", + "shortDescription": { + "text": "Assignment to 'for' loop parameter" + }, + "fullDescription": { + "text": "Reports assignments to for loop parameters inside the for loop body. While occasionally intended, this construct can be extremely confusing, and is often the result of a typo. Example: 'for (value in [1, 2, 3]) {\n value = 4 // warning\n }'", + "markdown": "Reports assignments to **for** loop parameters inside the **for** loop body.\n\nWhile occasionally intended, this construct can be extremely confusing, and is often the result of a typo.\n\n**Example:**\n\n\n for (value in [1, 2, 3]) {\n value = 4 // warning\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyAssignmentToForLoopParameter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Assignment issues", + "index": 67, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GrUnnecessaryAlias", + "shortDescription": { + "text": "Unnecessary import alias" + }, + "fullDescription": { + "text": "Reports unnecessary import aliases. Example: 'import com.foo.Bar as Bar' After the quick-fix is applied: 'import com.foo.Bar'", + "markdown": "Reports unnecessary import aliases.\n\n**Example:**\n\n\n import com.foo.Bar as Bar\n\nAfter the quick-fix is applied:\n\n\n import com.foo.Bar\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "GrUnnecessaryAlias", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Style", + "index": 84, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyAssignmentCanBeOperatorAssignment", + "shortDescription": { + "text": "Assignment can be replaced with operator assignment" + }, + "fullDescription": { + "text": "Reports assignments which can be replaced by an operator assignment. Example: 'a = a + b' After the quick-fix is applied: 'a += b' Configure the inspection: Use the Ignore conditional operators option to ignore '&&' and '||' operators. Use the Ignore obscure operators option to ignore '^' and '%' operators.", + "markdown": "Reports assignments which can be replaced by an operator assignment.\n\n**Example:**\n\n\n a = a + b\n\nAfter the quick-fix is applied:\n\n\n a += b\n\nConfigure the inspection:\n\n* Use the **Ignore conditional operators** option to ignore `&&` and `||` operators.\n* Use the **Ignore obscure operators** option to ignore `^` and `%` operators." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "GroovyAssignmentCanBeOperatorAssignment", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Assignment issues", + "index": 67, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyImplicitNullArgumentCall", + "shortDescription": { + "text": "Implicit null argument" + }, + "fullDescription": { + "text": "Reports calls with no arguments to method that has exactly one parameter. This is equivalent to call with 'null', and that behavior is often confusing and unintended. Example: 'def foo(String s){}\nfoo() // this call is actually 'foo(null)' call'", + "markdown": "Reports calls with no arguments to method that has exactly one parameter. This is equivalent to call with `null`, and that behavior is often confusing and unintended.\n\n**Example:**\n\n\n def foo(String s){}\n foo() // this call is actually 'foo(null)' call\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "GroovyImplicitNullArgumentCall", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Potentially confusing code constructs", + "index": 74, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyReturnFromFinallyBlock", + "shortDescription": { + "text": "'return' inside 'finally' block" + }, + "fullDescription": { + "text": "Reports 'return' statements inside of 'finally' blocks. While occasionally intended, such 'return' statements may mask exceptions thrown, and complicate debugging.", + "markdown": "Reports `return` statements inside of `finally` blocks.\n\n\nWhile occasionally intended, such `return` statements may mask exceptions thrown, and\ncomplicate debugging." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyReturnFromFinallyBlock", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Error handling", + "index": 43, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyConstructorNamedArguments", + "shortDescription": { + "text": "Named arguments of constructor call" + }, + "fullDescription": { + "text": "Reports named arguments of a default class constructor call which don't correspond to properties of this class. Example: 'class Person {\n def name\n def age\n }\n\n // 'firstName' property doesn't exist\n new Person(firstName: \"John\")'", + "markdown": "Reports named arguments of a default class constructor call which don't correspond to properties of this class.\n\n**Example:**\n\n\n class Person {\n def name\n def age\n }\n\n // 'firstName' property doesn't exist\n new Person(firstName: \"John\")\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyConstructorNamedArguments", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Probable bugs", + "index": 55, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyConditionalWithIdenticalBranches", + "shortDescription": { + "text": "Ternary expression with identical branches" + }, + "fullDescription": { + "text": "Reports ternary expressions with identical \"then\" and \"else\" branches. Such expressions are almost certainly a programmer error. The quick-fix replaces the expression with its \"then\" branch. Example: 'condition ? a.foo() : a.foo()' After the quick-fix is applied: 'a.foo()'", + "markdown": "Reports ternary expressions with identical \"then\" and \"else\" branches. Such expressions are almost certainly a programmer error.\n\nThe quick-fix replaces the expression with its \"then\" branch.\n\n**Example:**\n\n\n condition ? a.foo() : a.foo()\n\nAfter the quick-fix is applied:\n\n\n a.foo()\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyConditionalWithIdenticalBranches", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Control flow issues", + "index": 72, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GrUnnecessaryNonSealedModifier", + "shortDescription": { + "text": "Unnecessary 'non-sealed' modifier" + }, + "fullDescription": { + "text": "Reports unnecessary 'non-sealed' modifiers which used on methods, fields, or variables. This modifier has effect only on classes, interfaces and traits. Example: 'non-sealed boolean foo() {} // modifier is unnecessary\n non-sealed Object bar // modifier is unnecessary\n\n // modifier is required and therefore not highlighted\n non-sealed class A {}'", + "markdown": "Reports unnecessary `non-sealed` modifiers which used on methods, fields, or variables.\n\nThis modifier has effect only on classes, interfaces and traits.\n\n**Example:**\n\n\n non-sealed boolean foo() {} // modifier is unnecessary\n non-sealed Object bar // modifier is unnecessary\n\n // modifier is required and therefore not highlighted\n non-sealed class A {}\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GrUnnecessaryNonSealedModifier", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Style", + "index": 84, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GrAnnotationReferencingUnknownIdentifiers", + "shortDescription": { + "text": "@TupleConstructor and @MapConstructor" + }, + "fullDescription": { + "text": "Reports unresolved identifiers in '@TupleConstructor' and '@MapConstructor' 'includes' and 'excludes' annotation attribute values. Example: '// unresolved 'c'\n @TupleConstructor(includes = ['a', 'b', 'c'])\n class X {\n def a\n def b\n }'", + "markdown": "Reports unresolved identifiers in `@TupleConstructor` and `@MapConstructor` `includes` and `excludes` annotation attribute values.\n\n**Example:**\n\n\n // unresolved 'c'\n @TupleConstructor(includes = ['a', 'b', 'c'])\n class X {\n def a\n def b\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GrAnnotationReferencingUnknownIdentifiers", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Annotations", + "index": 91, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GrUnnecessaryDefModifier", + "shortDescription": { + "text": "Unnecessary 'def'" + }, + "fullDescription": { + "text": "Reports unnecessary 'def' modifiers when used with explicit type declaration. Example: 'def boolean foo() {} // modifier is unnecessary\n def Object bar // modifier is unnecessary\n\n // modifier is required and therefore not highlighted\n def (int a, String b) = []'", + "markdown": "Reports unnecessary `def` modifiers when used with explicit type declaration.\n\n**Example:**\n\n\n def boolean foo() {} // modifier is unnecessary\n def Object bar // modifier is unnecessary\n\n // modifier is required and therefore not highlighted\n def (int a, String b) = []\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GrUnnecessaryDefModifier", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Style", + "index": 84, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GrUnresolvedAccess", + "shortDescription": { + "text": "Unresolved reference expression" + }, + "fullDescription": { + "text": "Reports reference expressions which cannot be resolved.", + "markdown": "Reports reference expressions which cannot be resolved." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "GrUnresolvedAccess", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Probable bugs", + "index": 55, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GrPOJO", + "shortDescription": { + "text": "@POJO without @CompileStatic" + }, + "fullDescription": { + "text": "Reports annotation '@groovy.transform.stc.POJO' applied without '@groovy.transform.CompileStatic'. Annotation '@POJO' changes compilation process of Groovy classes to bytecode. It has no effect without explicitly enabled static compilation (which is done via '@CompileStatic' annotation). Example: '@POJO // reports @POJO\n class A {}'", + "markdown": "Reports annotation `@groovy.transform.stc.POJO` applied without `@groovy.transform.CompileStatic`.\n\nAnnotation `@POJO` changes compilation process of Groovy classes to bytecode. It has no effect without explicitly enabled static compilation (which is done via `@CompileStatic` annotation).\n\n**Example:**\n\n\n @POJO // reports @POJO\n class A {}\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GrPOJO", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Annotations", + "index": 91, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyWaitWhileNotSynchronized", + "shortDescription": { + "text": "'wait()' while not synced" + }, + "fullDescription": { + "text": "Reports calls to 'wait()' not made inside a corresponding synchronized statement or synchronized method. Calling 'wait()' on an object without holding a lock on that object will result in an 'IllegalMonitorStateException' being thrown. Such a construct is not necessarily an error, as the necessary lock may be acquired before the containing method is called, but its worth looking at.", + "markdown": "Reports calls to `wait()` not made inside a corresponding synchronized\nstatement or synchronized method.\n\nCalling `wait()` on an object\nwithout holding a lock on that object will result in an `IllegalMonitorStateException` being thrown.\nSuch a construct is not necessarily an error, as the necessary lock may be acquired before\nthe containing method is called, but its worth looking at." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyWaitWhileNotSynchronized", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Threading issues", + "index": 38, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyMultipleReturnPointsPerMethod", + "shortDescription": { + "text": "Method with multiple return points" + }, + "fullDescription": { + "text": "Reports methods with too many return points. Methods with too many return points may be confusing, and hard to refactor. Example: 'int foo(int a) {\n if (a > 0) {\n return a\n }\n if (a < 0) return -a\n return 0\n }'\n Use the field provided below to specify the maximum acceptable number of return points a method might have.", + "markdown": "Reports methods with too many return points. Methods with too many return points may be confusing, and hard to refactor.\n\n**Example:**\n\n\n int foo(int a) {\n if (a > 0) {\n return a\n }\n if (a < 0) return -a\n return 0\n }\n\n\nUse the field provided below to specify the maximum acceptable number of return points a method\nmight have." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyMultipleReturnPointsPerMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Method metrics", + "index": 107, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyConditionalCanBeElvis", + "shortDescription": { + "text": "Ternary expression can be replaced with elvis expression" + }, + "fullDescription": { + "text": "Reports ternary expressions which can be replaced by an elvis expression. Example: 'def notNull(o, defaultValue) {\n o != null ? o : defaultValue\n }' After the quick-fix is applied: 'def notNull(o, defaultValue) {\n o ?: defaultValue\n }'", + "markdown": "Reports ternary expressions which can be replaced by an elvis expression.\n\n**Example:**\n\n\n def notNull(o, defaultValue) {\n o != null ? o : defaultValue\n }\n\nAfter the quick-fix is applied:\n\n\n def notNull(o, defaultValue) {\n o ?: defaultValue\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "GroovyConditionalCanBeElvis", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Control flow issues", + "index": 72, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GrUnnecessaryFinalModifier", + "shortDescription": { + "text": "Unnecessary 'final'" + }, + "fullDescription": { + "text": "Reports unnecessary 'final' modifiers when used with the record definition. Example: 'final record R(int a) {} // modifier is unnecessary'", + "markdown": "Reports unnecessary `final` modifiers when used with the record definition.\n\n**Example:**\n\n\n final record R(int a) {} // modifier is unnecessary\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GrUnnecessaryFinalModifier", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Style", + "index": 84, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyOverlyLongMethod", + "shortDescription": { + "text": "Overly long method" + }, + "fullDescription": { + "text": "Reports methods that are too long. Methods that are too long may be confusing, and are a good sign that refactoring is necessary. Use the Maximum statements per method field to specify the maximum acceptable number of non-comment source statements a method might have.", + "markdown": "Reports methods that are too long.\n\n\nMethods that are too long\nmay be confusing, and are a good sign that refactoring is necessary.\n\n\nUse the **Maximum statements per method** field to specify the maximum acceptable number of non-comment source\nstatements a method might have." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyOverlyLongMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Method metrics", + "index": 107, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SecondUnsafeCall", + "shortDescription": { + "text": "Second unsafe call" + }, + "fullDescription": { + "text": "Reports possible NullPointerException during chain methods or properties call. Example: 'domain?.getZone().getName()' After the quick-fix is applied: 'domain?.getZone()?.getName()'", + "markdown": "Reports possible **NullPointerException** during chain methods or properties call.\n\n**Example:**\n\n\n domain?.getZone().getName()\n\nAfter the quick-fix is applied:\n\n\n domain?.getZone()?.getName()\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SecondUnsafeCall", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Probable bugs", + "index": 55, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GrPackage", + "shortDescription": { + "text": "Package mismatch" + }, + "fullDescription": { + "text": "Reports files with a declared package that does not match the package expected. Also, reports files without 'package' statements if the class is not located directly in the source root directory.", + "markdown": "Reports files with a declared package that does not match the package expected. Also, reports files without `package` statements if the class is not located directly in the source root directory." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GrPackage", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Potentially confusing code constructs", + "index": 74, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyOverlyComplexBooleanExpression", + "shortDescription": { + "text": "Overly complex boolean expression" + }, + "fullDescription": { + "text": "Reports boolean expressions with too many terms. Such expressions may be confusing and bug-prone. Use the Maximum number of terms field to specify the maximum number of terms allowed in a boolean expression.", + "markdown": "Reports boolean expressions with too many terms.\n\n\nSuch expressions may be confusing and bug-prone.\n\n\nUse the **Maximum number of terms** field to specify the maximum number of terms allowed in a boolean expression." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyOverlyComplexBooleanExpression", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Potentially confusing code constructs", + "index": 74, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyUnreachableStatement", + "shortDescription": { + "text": "Unreachable statement" + }, + "fullDescription": { + "text": "Reports statements that are unreachable. This can occur if the statement is after an infinite loop, 'return', 'break', or 'continue' statement. Example: 'void foo (int n) {\n if (n < 1) {\n return\n print('This statement is unreachable')\n }\n while (true){\n print ('Hello, world!')\n }\n print('This statement is unreachable too')\n }'", + "markdown": "Reports statements that are unreachable. This can occur if the statement is after an infinite loop,\n`return`, `break`, or `continue` statement.\n\n**Example:**\n\n\n void foo (int n) {\n if (n < 1) {\n return\n print('This statement is unreachable')\n }\n while (true){\n print ('Hello, world!')\n }\n print('This statement is unreachable too')\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyUnreachableStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Validity issues", + "index": 101, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovySystemRunFinalizersOnExit", + "shortDescription": { + "text": "Call to System.runFinalizersOnExit()" + }, + "fullDescription": { + "text": "Reports calls to 'System.runFinalizersOnExit()'. This call is one of the most dangerous in the Java language. It is inherently non-thread-safe, may result in data corruption, deadlock, and may affect parts of the program far removed from its call point. It is deprecated, and its use is strongly discouraged.", + "markdown": "Reports calls to `System.runFinalizersOnExit()`.\n\n\nThis call is one of the most dangerous in the Java language. It is inherently non-thread-safe,\nmay result in data corruption, deadlock, and may affect parts of the program far removed from its call point.\nIt is deprecated, and its use is strongly discouraged." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovySystemRunFinalizersOnExit", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Threading issues", + "index": 38, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyInstanceVariableNamingConvention", + "shortDescription": { + "text": "Instance variable naming convention" + }, + "fullDescription": { + "text": "Reports instance variables whose names are too short, too long, or do not follow the specified regular expression pattern. Use the fields provided below to specify minimum length, maximum length and regular expression expected for instance variable names. Regular expressions should be specified in the standard 'java.util.regex' format.", + "markdown": "Reports instance variables whose names are too short, too long, or do not follow the specified regular expression pattern.\n\n\nUse the fields provided below to specify minimum length, maximum length and regular expression expected for\ninstance variable names.\n\nRegular expressions should be specified in the standard `java.util.regex` format." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyInstanceVariableNamingConvention", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Naming conventions", + "index": 35, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyConstantIfStatement", + "shortDescription": { + "text": "Constant if statement" + }, + "fullDescription": { + "text": "Reports 'if' statements with boolean constant as a condition. Example: 'if (true) {\n // ...\n }\n if (false) {\n // ...\n }'", + "markdown": "Reports `if` statements with boolean constant as a condition.\n\n**Example:**\n\n\n if (true) {\n // ...\n }\n if (false) {\n // ...\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyConstantIfStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Control flow issues", + "index": 72, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyParameterNamingConvention", + "shortDescription": { + "text": "Method parameter naming convention" + }, + "fullDescription": { + "text": "Reports method parameters whose names are either too short, too long, or do not follow the specified regular expression pattern. Use the fields provided below to specify minimum length, maximum length and regular expression expected for method parameter names. Regular expressions should be specified in the standard 'java.util.regex' format.", + "markdown": "Reports method parameters whose names are either too short, too long, or do not follow the specified regular expression pattern.\n\n\nUse the fields provided below to specify minimum length, maximum length and regular expression\nexpected for method parameter names.\n\nRegular expressions should be specified in the standard `java.util.regex` format." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyParameterNamingConvention", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Naming conventions", + "index": 35, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GrUnnecessaryPublicModifier", + "shortDescription": { + "text": "Unnecessary 'public'" + }, + "fullDescription": { + "text": "Reports unnecessary 'public' modifiers as Groovy classes and methods are 'public' by default. Example: 'public class Foo{\n public void bar(){\n }\n }' After the quick-fix is applied: 'class Foo{\n void bar(){\n }\n }'", + "markdown": "Reports unnecessary `public` modifiers as Groovy classes and methods are `public` by default.\n\n**Example:**\n\n\n public class Foo{\n public void bar(){\n }\n }\n\nAfter the quick-fix is applied:\n\n\n class Foo{\n void bar(){\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GrUnnecessaryPublicModifier", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Style", + "index": 84, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GrStringStyleViolation", + "shortDescription": { + "text": "String style violation" + }, + "fullDescription": { + "text": "Reports strings with quotation that doesn't match code style. Example: 'def hw = \"Hello, world!\"' After the quick-fix is applied: 'def hw = 'Hello, world!'' Use the fields provided below to specify code style for different kinds of strings.", + "markdown": "Reports strings with quotation that doesn't match code style.\n\n**Example:**\n\n\n def hw = \"Hello, world!\"\n\nAfter the quick-fix is applied:\n\n\n def hw = 'Hello, world!'\n\nUse the fields provided below to specify code style for different kinds of strings." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "GrStringStyleViolation", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Style", + "index": 84, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyPointlessBoolean", + "shortDescription": { + "text": "Pointless boolean expression" + }, + "fullDescription": { + "text": "Reports pointless or pointlessly complicated boolean expressions. Such expressions include conjunction with true, disjunction with false, equality comparison with a boolean literal, or negation of a boolean literal. Such expressions may be the result of automated refactorings not completely followed through to completion, and in any case are unlikely to be what the developer intended to do. Example: 'if (someBool && true) {}' After the quick-fix is applied: 'if (someBool) {}'", + "markdown": "Reports pointless or pointlessly complicated boolean expressions.\n\n\nSuch expressions include conjunction with true,\ndisjunction with false,\nequality comparison with a boolean literal, or negation of a boolean literal. Such expressions may be\nthe result of automated refactorings\nnot completely followed through to completion, and in any case are unlikely to be what the developer\nintended to do.\n\n**Example:**\n\n\n if (someBool && true) {}\n\nAfter the quick-fix is applied:\n\n\n if (someBool) {}\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyPointlessBoolean", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Potentially confusing code constructs", + "index": 74, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SingletonConstructor", + "shortDescription": { + "text": "@Singleton constructors" + }, + "fullDescription": { + "text": "Reports constructors of classes annotated by '@Singleton' unless it is declared non-strict. Example: '@Singleton\n class Foo{\n Foo(){\n }\n }' There are two possible quick-fixes: either to remove the constructor or to declare '@Singleton' non-strict. After the quick-fix is applied: '@Singleton\n class Foo{\n }' or: '@Singleton(strict = false)\n class Foo{\n Foo(){\n }\n }'", + "markdown": "Reports constructors of classes annotated by `@Singleton` unless it is declared non-strict.\n\n**Example:**\n\n\n @Singleton\n class Foo{\n Foo(){\n }\n }\n\nThere are two possible quick-fixes: either to remove the constructor or to declare `@Singleton` non-strict.\n\nAfter the quick-fix is applied:\n\n\n @Singleton\n class Foo{\n }\n\nor:\n\n\n @Singleton(strict = false)\n class Foo{\n Foo(){\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "SingletonConstructor", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Annotations", + "index": 91, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyStaticVariableNamingConvention", + "shortDescription": { + "text": "Static variable naming convention" + }, + "fullDescription": { + "text": "Reports 'static' variables whose names are too short, too long, or do not follow the specified regular expression pattern. Constants, i.e. variables of immutable type declared 'static final', are not checked by this inspection Use the fields provided below to specify minimum length, maximum length and regular expression expected for static variable names. Regular expressions should be specified in the standard 'java.util.regex' format.", + "markdown": "Reports `static` variables whose names are too short, too long, or do not follow the specified regular expression pattern.\n\n\nConstants, i.e. variables of immutable type declared\n`static final`, are not checked by this inspection\n\n\nUse the fields provided below to specify minimum length, maximum length and regular expression expected for static variable names.\n\nRegular expressions should be specified in the standard `java.util.regex` format." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyStaticVariableNamingConvention", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Naming conventions", + "index": 35, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyInfiniteLoopStatement", + "shortDescription": { + "text": "Infinite loop statement" + }, + "fullDescription": { + "text": "Reports 'for', 'while', or 'do' statements which can only exit by throwing an exception. While such statements may be correct, they usually happen by mistake. Example: 'while(true) {\n Thread.sleep(1000)\n}'", + "markdown": "Reports `for`, `while`, or `do` statements which can only exit by throwing an exception. While such statements may be correct, they usually happen by mistake.\n\n**Example:**\n\n\n while(true) {\n Thread.sleep(1000)\n }\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyInfiniteLoopStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Probable bugs", + "index": 55, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyEmptyTryBlock", + "shortDescription": { + "text": "Empty 'try' block" + }, + "fullDescription": { + "text": "Reports empty 'try' blocks. Empty 'try' blocks usually indicate coding errors. Example: 'try {\n}\nfinally {\n close()\n}'", + "markdown": "Reports empty `try` blocks. Empty `try` blocks usually indicate coding errors.\n\n**Example:**\n\n\n try {\n }\n finally {\n close()\n }\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyEmptyTryBlock", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Error handling", + "index": 43, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GrPermitsClause", + "shortDescription": { + "text": "Non-extending permitted subclasses" + }, + "fullDescription": { + "text": "Reports permitted classes that do not extend the sealed base class. Groovy does not require that all permitted classes should be available in compile-time and compiled along with base class. Compiler will not warn the user on dealing with non-extending permitted subclass, but it contradicts the nature of sealed classes. Example: 'class A permits B {} // reports B\n class B {}'", + "markdown": "Reports permitted classes that do not extend the sealed base class.\n\nGroovy does not require that all permitted classes should be available in compile-time and compiled along with base class. Compiler will not warn the user on dealing with non-extending permitted subclass, but it contradicts the nature of sealed classes.\n\n**Example:**\n\n\n class A permits B {} // reports B\n class B {}\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "GrPermitsClause", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Probable bugs", + "index": 55, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyUnusedIncOrDec", + "shortDescription": { + "text": "Unused incrementing or decrementing" + }, + "fullDescription": { + "text": "Reports unused incrementing and decrementing expressions.", + "markdown": "Reports unused incrementing and decrementing expressions." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyUnusedIncOrDec", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Data flow", + "index": 106, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyEmptyCatchBlock", + "shortDescription": { + "text": "Empty 'catch' block" + }, + "fullDescription": { + "text": "Reports empty 'catch' blocks. While occasionally intended, empty 'catch' blocks can make debugging extremely difficult. Example: 'try {\n throw new Exception()\n}\ncatch (Exception e) {\n}'\n After the quick-fix is applied: 'try {\n throw new Exception()\n}\ncatch (Exception ignored) {\n}'", + "markdown": "Reports empty `catch` blocks. While occasionally intended, empty `catch` blocks can make debugging extremely difficult.\n\n**Example:**\n\n\n try {\n throw new Exception()\n }\n catch (Exception e) {\n }\n\nAfter the quick-fix is applied:\n\n\n try {\n throw new Exception()\n }\n catch (Exception ignored) {\n }\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyEmptyCatchBlock", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Error handling", + "index": 43, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ClashingTraitMethods", + "shortDescription": { + "text": "Clashing trait methods" + }, + "fullDescription": { + "text": "Reports classes which implement two or more traits that contain methods with same signatures. The result of calling such methods might be unexpected. The quick-fix adds an explicit overriding method. Example: 'trait T1 {\n def foo() {}\n }\n\n trait T2 {\n def foo() {}\n }\n\n class X implements T1, T2 {}\n\n // T2.foo() will be called\n new X().foo()' After the quick-fix is applied: 'class X implements T1, T2 {\n @Override\n Object foo() {\n return T2.super.foo()\n }\n }'", + "markdown": "Reports classes which implement two or more traits that contain methods with same signatures.\n\nThe result of calling such methods might be unexpected.\n\nThe quick-fix adds an explicit overriding method.\n\n**Example:**\n\n\n trait T1 {\n def foo() {}\n }\n\n trait T2 {\n def foo() {}\n }\n\n class X implements T1, T2 {}\n\n // T2.foo() will be called\n new X().foo()\n\nAfter the quick-fix is applied:\n\n\n class X implements T1, T2 {\n @Override\n Object foo() {\n return T2.super.foo()\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ClashingTraitMethods", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Potentially confusing code constructs", + "index": 74, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyOverlyComplexMethod", + "shortDescription": { + "text": "Overly complex method" + }, + "fullDescription": { + "text": "Reports methods that have too high a cyclomatic complexity. Cyclomatic complexity is basically a measurement of the number of branching points in a method. Methods with too high a cyclomatic complexity may be confusing and difficult to test. Use the Method complexity limit field to specify the maximum acceptable cyclomatic complexity a method might have.", + "markdown": "Reports methods that have too high a cyclomatic complexity.\n\n\nCyclomatic\ncomplexity is basically a measurement of the number of branching points in a method. Methods with too high\na cyclomatic complexity may be confusing and difficult to test.\n\n\nUse the **Method complexity limit** field to specify the maximum acceptable cyclomatic complexity a method might have." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyOverlyComplexMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Method metrics", + "index": 107, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TypeCustomizer", + "shortDescription": { + "text": "Type customizer inspection" + }, + "fullDescription": { + "text": "Reports files which can be custom type checkers and are not added to compiler resources yet.", + "markdown": "Reports files which can be custom type checkers and are not added to compiler resources yet." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "TypeCustomizer", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Other", + "index": 81, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyOverlyNestedMethod", + "shortDescription": { + "text": "Overly nested method" + }, + "fullDescription": { + "text": "Reports methods whose bodies are too deeply nested. Methods with too much statement nesting may be confusing, and are a good sign that refactoring may be necessary. Use the Maximum nesting depth field to specify the maximum acceptable nesting depth a method might have.", + "markdown": "Reports methods whose bodies are too deeply nested.\n\n\nMethods with too much statement\nnesting may be confusing, and are a good sign that refactoring may be necessary.\n\n\nUse the **Maximum nesting depth** field to specify the maximum acceptable nesting depth a method might have." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyOverlyNestedMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Method metrics", + "index": 107, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyVariableNotAssigned", + "shortDescription": { + "text": "Variable not assigned" + }, + "fullDescription": { + "text": "Reports variables that might not have been initialized.", + "markdown": "Reports variables that might not have been initialized." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyVariableNotAssigned", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Data flow", + "index": 106, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyMethodWithMoreThanThreeNegations", + "shortDescription": { + "text": "Method with more than three negations" + }, + "fullDescription": { + "text": "Reports methods with three or more negation operations ('!' or '!='). Such methods may be unnecessarily confusing.", + "markdown": "Reports methods with three or more negation operations (`!` or `!=`). Such methods may be unnecessarily confusing." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyMethodWithMoreThanThreeNegations", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Method metrics", + "index": 107, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyDoubleNegation", + "shortDescription": { + "text": "Double negation" + }, + "fullDescription": { + "text": "Reports double negation that can be simplified. Example: 'if (!!functionCall()) {} // double negation\nif (!(a != b)) {} // double negation'\n After the quick-fix is applied: 'if (functionCall()) {}\nif (a == b) {}'", + "markdown": "Reports double negation that can be simplified.\n\n**Example:**\n\n\n if (!!functionCall()) {} // double negation\n if (!(a != b)) {} // double negation\n\nAfter the quick-fix is applied:\n\n\n if (functionCall()) {}\n if (a == b) {}\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyDoubleNegation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Potentially confusing code constructs", + "index": 74, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GrNamedVariantLabels", + "shortDescription": { + "text": "@NamedVariant/@NamedParam/@NamedDelegate unresolved label" + }, + "fullDescription": { + "text": "Reports unresolved argument labels in calls of methods annotated by '@NamedVariant'/'@NamedParam'/'@NamedDelegate'. Example: '@groovy.transform.NamedVariant\n def foo(a, b) {}\n\n // unresolved label 'c'\n foo(a: 1, b: 2, c: 3)'", + "markdown": "Reports unresolved argument labels in calls of methods annotated by `@NamedVariant`/`@NamedParam`/`@NamedDelegate`.\n\n**Example:**\n\n\n @groovy.transform.NamedVariant\n def foo(a, b) {}\n\n // unresolved label 'c'\n foo(a: 1, b: 2, c: 3)\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GrNamedVariantLabels", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Annotations", + "index": 91, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyIfStatementWithTooManyBranches", + "shortDescription": { + "text": "If statement with too many branches" + }, + "fullDescription": { + "text": "Reports 'if' statements with too many branches. Such statements may be confusing, and are often the sign of inadequate levels of design abstraction. Example: 'if (a) {\n print \"foo\"\n} else if (b) {\n print \"bar\"\n} else if (c) {\n print \"baz\"\n} else if (d) {\n print \"Too many branches\"\n}'\n Use the Maximum number of branches field to specify the maximum number of branches expected.", + "markdown": "Reports `if` statements with too many branches. Such statements may be confusing, and are often the sign of inadequate levels of design abstraction.\n\n**Example:**\n\n\n if (a) {\n print \"foo\"\n } else if (b) {\n print \"bar\"\n } else if (c) {\n print \"baz\"\n } else if (d) {\n print \"Too many branches\"\n }\n\n\nUse the **Maximum number of branches** field to specify the maximum number of branches expected." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyIfStatementWithTooManyBranches", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Control flow issues", + "index": 72, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyNegatedConditional", + "shortDescription": { + "text": "Negated conditional expression" + }, + "fullDescription": { + "text": "Reports conditional expressions whose conditions are negated. Flipping the order of the conditional expression branches will usually increase the clarity of such statements. Example: '~condition ? \"1\" : \"2\"'", + "markdown": "Reports conditional expressions whose conditions are negated. Flipping the order of the conditional expression branches will usually increase the clarity of such statements.\n\n**Example:**\n\n\n ~condition ? \"1\" : \"2\"\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyNegatedConditional", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Potentially confusing code constructs", + "index": 74, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyWhileLoopSpinsOnField", + "shortDescription": { + "text": "While loop spins on field" + }, + "fullDescription": { + "text": "Reports 'while' loops, which spin on the value of a non-'volatile' field, waiting for it to be changed by another thread. In addition to being potentially extremely CPU intensive when little work is done inside the loop, such loops likely have different semantics than intended. The Java Memory Model allows that loop to never complete even if another thread changes the field's value. Example: 'class SpinsOnField {\n boolean ready = false;\n\n void run() {\n // the loop may never complete even after\n // markAsReady call from the other thread\n while (!ready) {\n }\n // do some work\n }\n\n void markAsReady() {\n ready = true;\n }\n }' Additionally since Java 9, calling 'Thread.onSpinWait()' inside spin loop on a 'volatile' field is recommended, which may significantly improve performance on some hardware. Use the checkbox below to have this inspection report only empty 'while' loops.", + "markdown": "Reports `while` loops, which spin on the\nvalue of a non-`volatile` field, waiting for it to be changed by another thread.\n\n\nIn addition to being potentially extremely CPU intensive when little work is done inside the loop, such\nloops likely have different semantics than intended. The Java Memory Model allows that loop to never complete even\nif another thread changes the field's value.\n\n**Example:**\n\n\n class SpinsOnField {\n boolean ready = false;\n\n void run() {\n // the loop may never complete even after\n // markAsReady call from the other thread\n while (!ready) {\n }\n // do some work\n }\n\n void markAsReady() {\n ready = true;\n }\n }\n\n\nAdditionally since Java 9, calling `Thread.onSpinWait()` inside spin loop\non a `volatile` field is recommended, which may significantly improve performance on some hardware.\n\n\nUse the checkbox below to have this inspection report only empty `while` loops." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyWhileLoopSpinsOnField", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Threading issues", + "index": 38, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GrEqualsBetweenInconvertibleTypes", + "shortDescription": { + "text": "'equals()' between objects of inconvertible types" + }, + "fullDescription": { + "text": "Reports calls to 'equals()' where the target and argument are of incompatible types. While such a call might theoretically be useful, most likely it represents a bug. Example: 'new HashSet() == new TreeSet())'", + "markdown": "Reports calls to `equals()` where the target and argument are of incompatible types.\n\nWhile such a call might theoretically be useful, most likely it represents a bug.\n\n**Example:**\n\n\n new HashSet() == new TreeSet())\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GrEqualsBetweenInconvertibleTypes", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Probable bugs", + "index": 55, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyEmptySyncBlock", + "shortDescription": { + "text": "Empty 'synchronized' block" + }, + "fullDescription": { + "text": "Reports 'synchronized' statements with empty bodies. While theoretically this may be the semantics intended, this construction is confusing, and often the result of a typo. Example: 'synchronized(lock) {\n}'", + "markdown": "Reports `synchronized` statements with empty bodies. While theoretically this may be the semantics intended, this construction is confusing, and often the result of a typo.\n\n**Example:**\n\n\n synchronized(lock) {\n }\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyEmptySyncBlock", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Threading issues", + "index": 38, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovySillyAssignment", + "shortDescription": { + "text": "Silly assignment" + }, + "fullDescription": { + "text": "Reports assignments of a variable to itself.", + "markdown": "Reports assignments of a variable to itself." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovySillyAssignment", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Assignment issues", + "index": 67, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyConditionalCanBeConditionalCall", + "shortDescription": { + "text": "Ternary expression can be replaced with safe call" + }, + "fullDescription": { + "text": "Reports ternary expressions which can be replaced by a safe call. Example: 'def charArray(String s) {\n s == null ? null : s.toCharArray()\n }' After the quick-fix is applied: 'def charArray(String s) {\n s?.toCharArray()\n }'", + "markdown": "Reports ternary expressions which can be replaced by a safe call.\n\n**Example:**\n\n\n def charArray(String s) {\n s == null ? null : s.toCharArray()\n }\n\nAfter the quick-fix is applied:\n\n\n def charArray(String s) {\n s?.toCharArray()\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "GroovyConditionalCanBeConditionalCall", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Control flow issues", + "index": 72, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyInfiniteRecursion", + "shortDescription": { + "text": "Infinite recursion" + }, + "fullDescription": { + "text": "Reports methods which must either recurse infinitely or throw an exception. Methods reported by this inspection could not be finished correct. Example: '// this function always dive deeper\ndef fibonacci(int n) {\n return fibonacci(n-1) + fibonacci(n-2)\n}'", + "markdown": "Reports methods which must either recurse infinitely or throw an exception. Methods reported by this inspection could not be finished correct.\n\n**Example:**\n\n\n // this function always dive deeper\n def fibonacci(int n) {\n return fibonacci(n-1) + fibonacci(n-2)\n }\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyInfiniteRecursion", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Probable bugs", + "index": 55, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyNestedSwitch", + "shortDescription": { + "text": "Nested switch statement" + }, + "fullDescription": { + "text": "Reports 'switch' statements that are nested inside other 'switch' statements. Such nested switch statements are confusing, and may result in unexpected behaviour. Example: 'switch (outer) {\n case 1:\n switch (inner) {\n case 1:\n print \"inner 1\"\n break\n default:\n print \"inner default\"\n }\n break\n default:\n print \"default\"\n}'", + "markdown": "Reports `switch` statements that are nested inside other `switch` statements. Such nested switch statements are confusing, and may result in unexpected behaviour.\n\n**Example:**\n\n\n switch (outer) {\n case 1:\n switch (inner) {\n case 1:\n print \"inner 1\"\n break\n default:\n print \"inner default\"\n }\n break\n default:\n print \"default\"\n }\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyNestedSwitch", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Potentially confusing code constructs", + "index": 74, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyRangeTypeCheck", + "shortDescription": { + "text": "Incorrect range arguments" + }, + "fullDescription": { + "text": "Reports types used in ranges that do not have a 'next()' or 'previous()' method or do not implement the 'java.lang.Comparable' interface.", + "markdown": "Reports types used in ranges that do not have a `next()` or `previous()` method or do not implement the `java.lang.Comparable` interface." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyRangeTypeCheck", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Probable bugs", + "index": 55, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GrUnnecessarySealedModifier", + "shortDescription": { + "text": "Unnecessary 'sealed' modifier" + }, + "fullDescription": { + "text": "Reports unnecessary 'sealed' modifiers which used on methods, fields, or variables. This modifier has effect only on classes, interfaces and traits. Example: 'sealed boolean foo() {} // modifier is unnecessary\n sealed Object bar // modifier is unnecessary\n\n // modifier is required and therefore not highlighted\n sealed class A {}'", + "markdown": "Reports unnecessary `sealed` modifiers which used on methods, fields, or variables.\n\nThis modifier has effect only on classes, interfaces and traits.\n\n**Example:**\n\n\n sealed boolean foo() {} // modifier is unnecessary\n sealed Object bar // modifier is unnecessary\n\n // modifier is required and therefore not highlighted\n sealed class A {}\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GrUnnecessarySealedModifier", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Style", + "index": 84, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GrDeprecatedAPIUsage", + "shortDescription": { + "text": "Deprecated API usage" + }, + "fullDescription": { + "text": "Reports references to deprecated classes, fields, and methods.", + "markdown": "Reports references to deprecated classes, fields, and methods." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GrDeprecatedAPIUsage", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Potentially confusing code constructs", + "index": 74, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyMissingReturnStatement", + "shortDescription": { + "text": "Missing return statement" + }, + "fullDescription": { + "text": "Reports missing 'return' statements at the end of methods with a non-void return type. The end of method should be reachable by the method's execution flow. Example: 'String foo(int a) {\n if (a > 0) {\n return \"more than zero\"\n }\n} // foo(-1) will return 'null'\n\nint bar(int a) {\n if (a > 0) {\n return a\n }\n} // bar(-1) will fall with runtime exception'", + "markdown": "Reports missing `return` statements at the end of methods with a non-**void** return type. The end of method should be reachable by the method's execution flow.\n\n**Example:**\n\n\n String foo(int a) {\n if (a > 0) {\n return \"more than zero\"\n }\n } // foo(-1) will return 'null'\n\n int bar(int a) {\n if (a > 0) {\n return a\n }\n } // bar(-1) will fall with runtime exception\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyMissingReturnStatement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Data flow", + "index": 106, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyPublicFieldAccessedInSynchronizedContext", + "shortDescription": { + "text": "Non-private field accessed in synchronized context" + }, + "fullDescription": { + "text": "Reports non-'final', non-'private' fields which are accessed in a synchronized context. A non-private field cannot be guaranteed to always be accessed in a synchronized manner, and such \"partially synchronized\" access may result in unexpectedly inconsistent data structures. Accesses in constructors an initializers are ignored for purposes of this inspection.", + "markdown": "Reports non-`final`, non-`private` fields which are accessed in a synchronized context.\n\n\nA non-private field cannot be guaranteed to always be accessed in a synchronized manner, and such \"partially synchronized\"\naccess may result in unexpectedly inconsistent data structures. Accesses in constructors an initializers are ignored\nfor purposes of this inspection." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyPublicFieldAccessedInSynchronizedContext", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Threading issues", + "index": 38, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyUnnecessaryContinue", + "shortDescription": { + "text": "Unnecessary 'continue' statement" + }, + "fullDescription": { + "text": "Reports 'continue' statements if they are last reachable statements in the loop. These 'continue' statements are unnecessary and can be safely removed. Example: 'for(int i in array) {\n println(i)\n continue\n }' After the quick-fix is applied: 'for(int i in array) {\n println(i)\n }' For more information, see the same inspection in Java.", + "markdown": "Reports `continue` statements if they are last reachable statements in the loop.\nThese `continue` statements are unnecessary and can be safely removed.\n\n**Example:**\n\n\n for(int i in array) {\n println(i)\n continue\n }\n\nAfter the quick-fix is applied:\n\n\n for(int i in array) {\n println(i)\n }\n\nFor more information, see the same inspection in Java." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyUnnecessaryContinue", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Control flow issues", + "index": 72, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyUnusedAssignment", + "shortDescription": { + "text": "Unused assignment" + }, + "fullDescription": { + "text": "Reports the cases where a variable is redundant as its value is never used after its assignment. If the variable is unused, we recommend removing it to shorten the code and to avoid redundant allocations. The following cases are reported: the variable never gets read after assignment the value is always overwritten with another assignment before the next variable read the variable initializer is redundant (for one of the above two reasons) For more info see the same inspection in Java.", + "markdown": "Reports the cases where a variable is redundant as its value is never used after its assignment.\n\nIf the variable is unused, we recommend removing it to shorten the code and to avoid redundant allocations.\n\nThe following cases are reported:\n\n* the variable never gets read after assignment\n* the value is always overwritten with another assignment before the next variable read\n* the variable initializer is redundant (for one of the above two reasons)\n\nFor more info see the same inspection in Java." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyUnusedAssignment", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Data flow", + "index": 106, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyNotifyWhileNotSynchronized", + "shortDescription": { + "text": "'notify()' or 'notifyAll()' while not synced" + }, + "fullDescription": { + "text": "Reports calls to 'notify()' and 'notifyAll()' not within a corresponding synchronized statement or synchronized method. Calling these methods on an object without holding a lock on that object will result in an 'IllegalMonitorStateException' being thrown. Such a construct is not necessarily an error, as the necessary lock may be acquired before the containing method is called, but it's worth looking at.", + "markdown": "Reports calls to `notify()` and `notifyAll()` not within a corresponding synchronized statement or synchronized method.\n\n\nCalling these methods on an object\nwithout holding a lock on that object will result in an `IllegalMonitorStateException` being thrown.\nSuch a construct is not necessarily an error, as the necessary lock may be acquired before\nthe containing method is called, but it's worth looking at." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyNotifyWhileNotSynchronized", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Threading issues", + "index": 38, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyBusyWait", + "shortDescription": { + "text": "Busy wait" + }, + "fullDescription": { + "text": "Reports calls to 'java.lang.Thread.sleep()' that occur inside loops. Such calls are indicative of \"busy-waiting\". Busy-waiting is often inefficient, and may result in unexpected deadlocks as busy-waiting threads do not release locked resources.", + "markdown": "Reports calls to `java.lang.Thread.sleep()` that occur inside loops.\n\n\nSuch calls are indicative of \"busy-waiting\". Busy-waiting is often inefficient, and may result in unexpected deadlocks\nas busy-waiting threads do not release locked resources." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyBusyWait", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Threading issues", + "index": 38, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyOctalInteger", + "shortDescription": { + "text": "Octal integer" + }, + "fullDescription": { + "text": "Reports octal integer literals. Some coding standards prohibit the use of octal literals, as they may be easily confused with decimal literals.", + "markdown": "Reports octal integer literals.\n\n\nSome coding standards prohibit the\nuse of octal literals, as they may be easily confused with decimal literals." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyOctalInteger", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Potentially confusing code constructs", + "index": 74, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyEmptyFinallyBlock", + "shortDescription": { + "text": "Empty 'finally' block" + }, + "fullDescription": { + "text": "Reports empty 'finally' blocks. Empty 'finally' blocks usually indicate coding errors. Example: 'try {\n throw new Exception()\n}\nfinally {\n}'", + "markdown": "Reports empty `finally` blocks. Empty `finally` blocks usually indicate coding errors.\n\n**Example:**\n\n\n try {\n throw new Exception()\n }\n finally {\n }\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyEmptyFinallyBlock", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Error handling", + "index": 43, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovySynchronizationOnVariableInitializedWithLiteral", + "shortDescription": { + "text": "Synchronization on variable initialized with literal" + }, + "fullDescription": { + "text": "Reports synchronized blocks which lock on an object which is initialized with a literal. String literals are interned and 'Number' literals can be allocated from a cache. Because of this, it is possible that some other part of the system which uses an object initialized with the same literal, is actually holding a reference to the exact same object. This can create unexpected dead-lock situations, if the string was thought to be private.", + "markdown": "Reports synchronized blocks which lock on an object which is initialized with a literal.\n\n\nString literals are interned and `Number` literals can be allocated from a cache. Because of\nthis, it is possible that some other part of the system which uses an object initialized with the same\nliteral, is actually holding a reference to the exact same object. This can create unexpected dead-lock\nsituations, if the string was thought to be private." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovySynchronizationOnVariableInitializedWithLiteral", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Threading issues", + "index": 38, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyUnusedDeclaration", + "shortDescription": { + "text": "Unused declaration" + }, + "fullDescription": { + "text": "Reports unused classes, methods and fields. Example: 'public class Department {\n private Organization myOrganization;\n }' Here 'Department' explicitly references 'Organization' but if 'Department' class itself is unused, then inspection would report both classes. The inspection also reports parameters, which are not used by their methods and all method implementations/overriders, as well as local variables, which are declared but not used. For more information, see the same inspection in Java.", + "markdown": "Reports unused classes, methods and fields.\n\n**Example:**\n\n\n public class Department {\n private Organization myOrganization;\n }\n\nHere `Department` explicitly references `Organization` but if `Department` class itself is unused,\nthen inspection would report both classes.\n\n\nThe inspection also reports parameters, which are not used by their methods and all method implementations/overriders, as well as local\nvariables, which are declared but not used.\n\nFor more information, see the same inspection in Java." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyUnusedDeclaration", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Declaration redundancy", + "index": 126, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyLoopStatementThatDoesntLoop", + "shortDescription": { + "text": "Loop statement that doesn't loop" + }, + "fullDescription": { + "text": "Reports 'for' or 'while' statements whose bodies are guaranteed to execute at most once. While such statements could be written intentionally, they are usually a symptom of error. Example: 'for (int i in 0..<10) {\n return\n }'", + "markdown": "Reports `for` or `while` statements whose bodies are guaranteed to execute at most once. While such statements could be written intentionally, they are usually a symptom of error.\n\n**Example:**\n\n\n for (int i in 0..<10) {\n return\n }\n\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyLoopStatementThatDoesntLoop", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Control flow issues", + "index": 72, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyResultOfIncrementOrDecrementUsed", + "shortDescription": { + "text": "Result of increment or decrement used" + }, + "fullDescription": { + "text": "Reports increment or decrement expressions nested inside other expressions. Such expressions may be confusing, and violate the general design principle that a given construct should do precisely one thing.", + "markdown": "Reports increment or decrement expressions nested inside other expressions.\n\n\nSuch expressions may be confusing, and violate the general design principle that a\ngiven construct should do precisely one thing." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyResultOfIncrementOrDecrementUsed", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Potentially confusing code constructs", + "index": 74, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovyReturnFromClosureCanBeImplicit", + "shortDescription": { + "text": "'return' statement can be implicit" + }, + "fullDescription": { + "text": "Reports return statements at the end of closures which can be made implicit. Groovy closures implicitly return the value of the last statement in them. Example: 'def foo = {\n return 1\n }' After the quick-fix is applied: 'def foo = {\n 1\n }'", + "markdown": "Reports return statements at the end of closures which can be made implicit.\n\n\nGroovy closures implicitly return the value of the last statement in them.\n\n**Example:**\n\n\n def foo = {\n return 1\n }\n\nAfter the quick-fix is applied:\n\n\n def foo = {\n 1\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovyReturnFromClosureCanBeImplicit", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Control flow issues", + "index": 72, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "GroovySynchronizationOnNonFinalField", + "shortDescription": { + "text": "Synchronization on non-final field" + }, + "fullDescription": { + "text": "Reports 'synchronized' statements where the lock expression is a non-'final' field. Such statements are unlikely to have useful semantics, as different threads may be locking on different objects even when operating on the same object.", + "markdown": "Reports `synchronized` statements where the lock expression is a non-`final` field.\n\n\nSuch statements are unlikely to have useful semantics, as different\nthreads may be locking on different objects even when operating on the same object." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "GroovySynchronizationOnNonFinalField", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Groovy/Threading issues", + "index": 38, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + } + ], + "language": "en-US", + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "isComprehensive": false + }, + { + "name": "com.intellij", + "version": "233.14714.228", + "rules": [ + { + "id": "HtmlUnknownBooleanAttribute", + "shortDescription": { + "text": "Incorrect boolean attribute" + }, + "fullDescription": { + "text": "Reports an HTML non-boolean attribute without a value. Suggests configuring attributes that should not be reported.", + "markdown": "Reports an HTML non-boolean attribute without a value. Suggests configuring attributes that should not be reported." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "HtmlUnknownBooleanAttribute", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "HTML", + "index": 29, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InconsistentLineSeparators", + "shortDescription": { + "text": "Inconsistent line separators" + }, + "fullDescription": { + "text": "Reports files with line separators different from the ones that are specified in the project's settings. For example, the inspection will be triggered if you set the line separator to '\\n' in Settings | Editor | Code Style | Line separator, while the file you are editing uses '\\r\\n' as a line separator. The inspection also warns you about mixed line separators within a file.", + "markdown": "Reports files with line separators different from the ones that are specified in the project's settings.\n\nFor example, the inspection will be triggered if you set the line separator to `\\n` in\n[Settings \\| Editor \\| Code Style \\| Line separator](settings://preferences.sourceCode?Line%20separator),\nwhile the file you are editing uses `\\r\\n` as a line separator.\n\nThe inspection also warns you about mixed line separators within a file." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "InconsistentLineSeparators", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "General", + "index": 44, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantSuppression", + "shortDescription": { + "text": "Redundant suppression" + }, + "fullDescription": { + "text": "Reports usages of the following elements that can be safely removed because the inspection they affect is no longer applicable in this context: '@SuppressWarning' annotation, or '// noinspection' line comment, or '/** noinspection */' JavaDoc comment Example: 'public class C {\n // symbol is already private,\n // but annotation is still around\n @SuppressWarnings({\"WeakerAccess\"})\n private boolean CONST = true;\n void f() {\n CONST = false;\n }\n}'", + "markdown": "Reports usages of the following elements that can be safely removed because the inspection they affect is no longer applicable in this context:\n\n* `@SuppressWarning` annotation, or\n* `// noinspection` line comment, or\n* `/** noinspection */` JavaDoc comment\n\nExample:\n\n\n public class C {\n // symbol is already private,\n // but annotation is still around\n @SuppressWarnings({\"WeakerAccess\"})\n private boolean CONST = true;\n void f() {\n CONST = false;\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantSuppression", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "General", + "index": 44, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ProblematicWhitespace", + "shortDescription": { + "text": "Problematic whitespace" + }, + "fullDescription": { + "text": "Reports the following problems: Tabs used for indentation when the code style is configured to use only spaces. Spaces used for indentation when the code style is configured to use only tabs. Spaces used for indentation and tabs used for alignment when the code style is configured to use smart tabs.", + "markdown": "Reports the following problems:\n\n* Tabs used for indentation when the code style is configured to use only spaces.\n* Spaces used for indentation when the code style is configured to use only tabs.\n* Spaces used for indentation and tabs used for alignment when the code style is configured to use smart tabs." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ProblematicWhitespace", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "General", + "index": 44, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "HtmlUnknownTarget", + "shortDescription": { + "text": "Unresolved file in a link" + }, + "fullDescription": { + "text": "Reports an unresolved file in a link.", + "markdown": "Reports an unresolved file in a link." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "HtmlUnknownTarget", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "HTML", + "index": 29, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LongLine", + "shortDescription": { + "text": "Line is longer than allowed by code style" + }, + "fullDescription": { + "text": "Reports lines that are longer than the Hard wrap at parameter specified in Settings | Editor | Code Style | General.", + "markdown": "Reports lines that are longer than the **Hard wrap at** parameter specified in [Settings \\| Editor \\| Code Style \\| General](settings://preferences.sourceCode?Hard%20wrap%20at)." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LongLine", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "General", + "index": 44, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "XmlWrongRootElement", + "shortDescription": { + "text": "Wrong root element" + }, + "fullDescription": { + "text": "Reports a root tag name different from the name specified in the '' tag.", + "markdown": "Reports a root tag name different from the name specified in the `` tag." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "XmlWrongRootElement", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "XML", + "index": 65, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "HtmlUnknownAttribute", + "shortDescription": { + "text": "Unknown attribute" + }, + "fullDescription": { + "text": "Reports an unknown HTML attribute. Suggests configuring attributes that should not be reported.", + "markdown": "Reports an unknown HTML attribute. Suggests configuring attributes that should not be reported." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "HtmlUnknownAttribute", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "HTML", + "index": 29, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RegExpRedundantEscape", + "shortDescription": { + "text": "Redundant character escape" + }, + "fullDescription": { + "text": "Reports redundant character escape sequences that can be replaced with unescaped characters preserving the meaning. Many escape sequences that are necessary outside of a character class are redundant inside square brackets '[]' of a character class. Although unescaped opening curly braces '{' outside of character classes are allowed in some dialects (JavaScript, Python, and so on), it can cause confusion and make the pattern less portable, because there are dialects that require escaping curly braces as characters. For this reason the inspection does not report escaped opening curly braces. Example: '\\-\\;[\\.]' After the quick-fix is applied: '-;[.]' The Ignore escaped closing brackets '}' and ']' option specifies whether to report '\\}' and '\\]' outside of a character class when they are allowed to be unescaped by the RegExp dialect. New in 2017.3", + "markdown": "Reports redundant character escape sequences that can be replaced with unescaped characters preserving the meaning. Many escape sequences that are necessary outside of a character class are redundant inside square brackets `[]` of a character class.\n\n\nAlthough unescaped opening curly braces `{` outside of character classes are allowed in some dialects (JavaScript, Python, and so on),\nit can cause confusion and make the pattern less portable, because there are dialects that require escaping curly braces as characters.\nFor this reason the inspection does not report escaped opening curly braces.\n\n**Example:**\n\n\n \\-\\;[\\.]\n\nAfter the quick-fix is applied:\n\n\n -;[.]\n\n\nThe **Ignore escaped closing brackets '}' and '\\]'** option specifies whether to report `\\}` and `\\]` outside of a character class\nwhen they are allowed to be unescaped by the RegExp dialect.\n\nNew in 2017.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RegExpRedundantEscape", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "RegExp", + "index": 83, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CustomRegExpInspection", + "shortDescription": { + "text": "Custom RegExp inspection" + }, + "fullDescription": { + "text": "Custom Regex Inspection", + "markdown": "Custom Regex Inspection" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "CustomRegExpInspection", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "RegExp", + "index": 83, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IncorrectFormatting", + "shortDescription": { + "text": "Incorrect formatting" + }, + "fullDescription": { + "text": "Reports formatting issues that appear if your code doesn't follow your project's code style settings. This inspection is not compatible with languages that require third-party formatters for code formatting, for example, Go or C with CLangFormat enabled.", + "markdown": "Reports formatting issues that appear if your code doesn't\nfollow your project's code style settings.\n\n\nThis inspection is not compatible with languages that require\nthird-party formatters for code formatting, for example, Go or\nC with CLangFormat enabled." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "IncorrectFormatting", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "General", + "index": 44, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "HtmlExtraClosingTag", + "shortDescription": { + "text": "Redundant closing tag" + }, + "fullDescription": { + "text": "Reports redundant closing tags on empty elements, for example, 'img' or 'br'. Example: '\n \n

\n \n ' After the quick-fix is applied: '\n \n
\n \n '", + "markdown": "Reports redundant closing tags on empty elements, for example, `img` or `br`.\n\n**Example:**\n\n\n \n \n

\n \n \n\nAfter the quick-fix is applied:\n\n\n \n \n
\n \n \n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "HtmlExtraClosingTag", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "HTML", + "index": 29, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RegExpUnexpectedAnchor", + "shortDescription": { + "text": "Begin or end anchor in unexpected position" + }, + "fullDescription": { + "text": "Reports '^' or '\\A' anchors not at the beginning of the pattern and '$', '\\Z' or '\\z' anchors not at the end of the pattern. In the wrong position these RegExp anchors prevent the pattern from matching anything. In case of the '^' and '$' anchors, most likely the literal character was meant and the escape forgotten. Example: '(Price $10)' New in 2018.1", + "markdown": "Reports `^` or `\\A` anchors not at the beginning of the pattern and `$`, `\\Z` or `\\z` anchors not at the end of the pattern. In the wrong position these RegExp anchors prevent the pattern from matching anything. In case of the `^` and `$` anchors, most likely the literal character was meant and the escape forgotten.\n\n**Example:**\n\n\n (Price $10)\n\n\nNew in 2018.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "RegExpUnexpectedAnchor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "RegExp", + "index": 83, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SpellCheckingInspection", + "shortDescription": { + "text": "Typo" + }, + "fullDescription": { + "text": "Reports typos and misspellings in your code, comments, and literals and fixes them with one click.", + "markdown": "Reports typos and misspellings in your code, comments, and literals and fixes them with one click." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "SpellCheckingInspection", + "ideaSeverity": "TYPO", + "qodanaSeverity": "Low" + } + }, + "relationships": [ + { + "target": { + "id": "Proofreading", + "index": 92, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CheckXmlFileWithXercesValidator", + "shortDescription": { + "text": "Failed external validation" + }, + "fullDescription": { + "text": "Reports a discrepancy in an XML file with the specified DTD or schema detected by the Xerces validator.", + "markdown": "Reports a discrepancy in an XML file with the specified DTD or schema detected by the Xerces validator." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "CheckXmlFileWithXercesValidator", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "XML", + "index": 65, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "HtmlUnknownTag", + "shortDescription": { + "text": "Unknown tag" + }, + "fullDescription": { + "text": "Reports an unknown HTML tag. Suggests configuring tags that should not be reported.", + "markdown": "Reports an unknown HTML tag. Suggests configuring tags that should not be reported." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "HtmlUnknownTag", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "HTML", + "index": 29, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RegExpEscapedMetaCharacter", + "shortDescription": { + "text": "Escaped meta character" + }, + "fullDescription": { + "text": "Reports escaped meta characters. Some RegExp coding styles specify that meta characters should be placed inside a character class, to make the regular expression easier to understand. This inspection does not warn about the meta character '[', ']' and '^', because those would need additional escaping inside a character class. Example: '\\d+\\.\\d+' After the quick-fix is applied: '\\d+[.]\\d+' New in 2017.1", + "markdown": "Reports escaped meta characters. Some RegExp coding styles specify that meta characters should be placed inside a character class, to make the regular expression easier to understand. This inspection does not warn about the meta character `[`, `]` and `^`, because those would need additional escaping inside a character class.\n\n**Example:**\n\n\n \\d+\\.\\d+\n\nAfter the quick-fix is applied:\n\n\n \\d+[.]\\d+\n\nNew in 2017.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RegExpEscapedMetaCharacter", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "RegExp", + "index": 83, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "XmlHighlighting", + "shortDescription": { + "text": "XML highlighting" + }, + "fullDescription": { + "text": "Reports XML validation problems in the results of a batch code inspection.", + "markdown": "Reports XML validation problems in the results of a batch code inspection." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "XmlHighlighting", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "XML", + "index": 65, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "XmlDuplicatedId", + "shortDescription": { + "text": "Duplicate 'id' attribute" + }, + "fullDescription": { + "text": "Reports a duplicate 'id' attribute in XML.", + "markdown": "Reports a duplicate `id` attribute in XML." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "XmlDuplicatedId", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "XML", + "index": 65, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RegExpDuplicateCharacterInClass", + "shortDescription": { + "text": "Duplicate character in character class" + }, + "fullDescription": { + "text": "Reports duplicate characters inside a RegExp character class. Duplicate characters are unnecessary and can be removed without changing the semantics of the regex. Example: '[aabc]' After the quick-fix is applied: '[abc]'", + "markdown": "Reports duplicate characters inside a RegExp character class. Duplicate characters are unnecessary and can be removed without changing the semantics of the regex.\n\n**Example:**\n\n\n [aabc]\n\nAfter the quick-fix is applied:\n\n\n [abc]\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "RegExpDuplicateCharacterInClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "RegExp", + "index": 83, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "XmlUnboundNsPrefix", + "shortDescription": { + "text": "Unbound namespace prefix" + }, + "fullDescription": { + "text": "Reports an unbound namespace prefix in XML.", + "markdown": "Reports an unbound namespace prefix in XML." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "XmlUnboundNsPrefix", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "XML", + "index": 65, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RequiredAttributes", + "shortDescription": { + "text": "Missing required attribute" + }, + "fullDescription": { + "text": "Reports a missing mandatory attribute in an XML/HTML tag. Suggests configuring attributes that should not be reported.", + "markdown": "Reports a missing mandatory attribute in an XML/HTML tag. Suggests configuring attributes that should not be reported." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RequiredAttributes", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "HTML", + "index": 29, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "XmlUnusedNamespaceDeclaration", + "shortDescription": { + "text": "Unused schema declaration" + }, + "fullDescription": { + "text": "Reports an unused namespace declaration or location hint in XML.", + "markdown": "Reports an unused namespace declaration or location hint in XML." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "XmlUnusedNamespaceDeclaration", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "XML", + "index": 65, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RegExpRedundantClassElement", + "shortDescription": { + "text": "Redundant '\\d', '[:digit:]', or '\\D' class elements" + }, + "fullDescription": { + "text": "Reports redundant '\\d' or '[:digit:]' that are used in one class with '\\w' or '[:word:]' ('\\D' with '\\W') and can be removed. Example: '[\\w\\d]' After the quick-fix is applied: '[\\w]' New in 2022.2", + "markdown": "Reports redundant `\\d` or `[:digit:]` that are used in one class with `\\w` or `[:word:]` (`\\D` with `\\W`) and can be removed.\n\n**Example:**\n\n\n [\\w\\d]\n\nAfter the quick-fix is applied:\n\n\n [\\w]\n\nNew in 2022.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "note", + "parameters": { + "suppressToolId": "RegExpRedundantClassElement", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "RegExp", + "index": 83, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RegExpSimplifiable", + "shortDescription": { + "text": "Regular expression can be simplified" + }, + "fullDescription": { + "text": "Reports regular expressions that can be simplified. Example: '[a] xx* [ah-hz]' After the quick-fix is applied: 'a x+ [ahz]' New in 2022.1", + "markdown": "Reports regular expressions that can be simplified.\n\n**Example:**\n\n\n [a] xx* [ah-hz]\n\nAfter the quick-fix is applied:\n\n\n a x+ [ahz]\n\nNew in 2022.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RegExpSimplifiable", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "RegExp", + "index": 83, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RegExpEmptyAlternationBranch", + "shortDescription": { + "text": "Empty branch in alternation" + }, + "fullDescription": { + "text": "Reports empty branches in a RegExp alternation. An empty branch will only match the empty string, and in most cases that is not what is desired. This inspection will not report a single empty branch at the start or the end of an alternation. Example: '(alpha||bravo)' After the quick-fix is applied: '(alpha|bravo)' New in 2017.2", + "markdown": "Reports empty branches in a RegExp alternation. An empty branch will only match the empty string, and in most cases that is not what is desired. This inspection will not report a single empty branch at the start or the end of an alternation.\n\n**Example:**\n\n\n (alpha||bravo)\n\nAfter the quick-fix is applied:\n\n\n (alpha|bravo)\n\nNew in 2017.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "RegExpEmptyAlternationBranch", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "RegExp", + "index": 83, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "XmlPathReference", + "shortDescription": { + "text": "Unresolved file reference" + }, + "fullDescription": { + "text": "Reports an unresolved file reference in XML.", + "markdown": "Reports an unresolved file reference in XML." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "XmlPathReference", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "XML", + "index": 65, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RegExpUnnecessaryNonCapturingGroup", + "shortDescription": { + "text": "Unnecessary non-capturing group" + }, + "fullDescription": { + "text": "Reports unnecessary non-capturing groups, which have no influence on the match result. Example: 'Everybody be cool, (?:this) is a robbery!' After the quick-fix is applied: 'Everybody be cool, this is a robbery!' New in 2021.1", + "markdown": "Reports unnecessary non-capturing groups, which have no influence on the match result.\n\n**Example:**\n\n\n Everybody be cool, (?:this) is a robbery!\n\nAfter the quick-fix is applied:\n\n\n Everybody be cool, this is a robbery!\n\nNew in 2021.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "RegExpUnnecessaryNonCapturingGroup", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "RegExp", + "index": 83, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TodoComment", + "shortDescription": { + "text": "TODO comment" + }, + "fullDescription": { + "text": "Reports TODO comments in your code. You can configure the format for TODO comments in Settings | Editor | TODO. Enable the Only warn on TODO comments without any details option to only warn on empty TODO comments, that don't provide any description on the task that should be done. Disable to report all TODO comments.", + "markdown": "Reports **TODO** comments in your code.\n\nYou can configure the format for **TODO** comments in [Settings \\| Editor \\| TODO](settings://preferences.toDoOptions).\n\nEnable the **Only warn on TODO comments without any details** option to only warn on empty TODO comments, that\ndon't provide any description on the task that should be done. Disable to report all TODO comments." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "TodoComment", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "General", + "index": 44, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Json5StandardCompliance", + "shortDescription": { + "text": "Compliance with JSON5 standard" + }, + "fullDescription": { + "text": "Reports inconsistency with the language specification in a JSON5 file.", + "markdown": "Reports inconsistency with [the language specification](http://json5.org) in a JSON5 file." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "Json5StandardCompliance", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "JSON and JSON5", + "index": 105, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "HtmlWrongAttributeValue", + "shortDescription": { + "text": "Wrong attribute value" + }, + "fullDescription": { + "text": "Reports an incorrect HTML attribute value.", + "markdown": "Reports an incorrect HTML attribute value." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "HtmlWrongAttributeValue", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "HTML", + "index": 29, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "XmlDefaultAttributeValue", + "shortDescription": { + "text": "Redundant attribute with default value" + }, + "fullDescription": { + "text": "Reports a redundant assignment of the default value to an XML attribute.", + "markdown": "Reports a redundant assignment of the default value to an XML attribute." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "XmlDefaultAttributeValue", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "XML", + "index": 65, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JsonSchemaCompliance", + "shortDescription": { + "text": "Compliance with JSON schema" + }, + "fullDescription": { + "text": "Reports inconsistence between a JSON file and the JSON schema that is assigned to it.", + "markdown": "Reports inconsistence between a JSON file and the [JSON schema](https://json-schema.org) that is assigned to it. " + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "JsonSchemaCompliance", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JSON and JSON5", + "index": 105, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EmptyDirectory", + "shortDescription": { + "text": "Empty directory" + }, + "fullDescription": { + "text": "Reports empty directories. Available only from Code | Inspect Code or Code | Analyze Code | Run Inspection by Name and isn't reported in the editor. Use the Only report empty directories located under a source folder option to have only directories under source roots reported.", + "markdown": "Reports empty directories.\n\nAvailable only from **Code \\| Inspect Code** or\n**Code \\| Analyze Code \\| Run Inspection by Name** and isn't reported in the editor.\n\nUse the **Only report empty directories located under a source folder** option to have only directories under source\nroots reported." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EmptyDirectory", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "General", + "index": 44, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CheckDtdRefs", + "shortDescription": { + "text": "Unresolved DTD reference" + }, + "fullDescription": { + "text": "Reports inconsistency in a DTD-specific reference, for example, in a reference to an XML entity or to a DTD element declaration. Works in DTD an XML files.", + "markdown": "Reports inconsistency in a DTD-specific reference, for example, in a reference to an XML entity or to a DTD element declaration. Works in DTD an XML files." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "CheckDtdRefs", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "XML", + "index": 65, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonAsciiCharacters", + "shortDescription": { + "text": "Non-ASCII characters" + }, + "fullDescription": { + "text": "Reports code elements that use non-ASCII symbols in an unusual context. Example: Non-ASCII characters used in identifiers, strings, or comments. Identifiers written in different languages, such as 'myСollection' with the letter 'C' written in Cyrillic. Comments or strings containing Unicode symbols, such as long dashes and arrows.", + "markdown": "Reports code elements that use non-ASCII symbols in an unusual context.\n\nExample:\n\n* Non-ASCII characters used in identifiers, strings, or comments.\n* Identifiers written in different languages, such as `my`**С**`ollection` with the letter **C** written in Cyrillic.\n* Comments or strings containing Unicode symbols, such as long dashes and arrows." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NonAsciiCharacters", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Internationalization", + "index": 109, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LossyEncoding", + "shortDescription": { + "text": "Lossy encoding" + }, + "fullDescription": { + "text": "Reports characters that cannot be displayed because of the current document encoding. Examples: If you type international characters in a document with the US-ASCII charset, some characters will be lost on save. If you load a UTF-8-encoded file using the ISO-8859-1 one-byte charset, some characters will be displayed incorrectly. You can fix this by changing the file encoding either by specifying the encoding directly in the file, e.g. by editing 'encoding=' attribute in the XML prolog of XML file, or by changing the corresponding options in Settings | Editor | File Encodings.", + "markdown": "Reports characters that cannot be displayed because of the current document encoding.\n\nExamples:\n\n* If you type international characters in a document with the **US-ASCII** charset, some characters will be lost on save.\n* If you load a **UTF-8** -encoded file using the **ISO-8859-1** one-byte charset, some characters will be displayed incorrectly.\n\nYou can fix this by changing the file encoding\neither by specifying the encoding directly in the file, e.g. by editing `encoding=` attribute in the XML prolog of XML file,\nor by changing the corresponding options in **Settings \\| Editor \\| File Encodings**." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LossyEncoding", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Internationalization", + "index": 109, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IgnoreFileDuplicateEntry", + "shortDescription": { + "text": "Ignore file duplicates" + }, + "fullDescription": { + "text": "Reports duplicate entries (patterns) in the ignore file (e.g. .gitignore, .hgignore). Duplicate entries in these files are redundant and can be removed. Example: '# Output directories\n /out/\n /target/\n /out/'", + "markdown": "Reports duplicate entries (patterns) in the ignore file (e.g. .gitignore, .hgignore). Duplicate entries in these files are redundant and can be removed.\n\nExample:\n\n\n # Output directories\n /out/\n /target/\n /out/\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "IgnoreFileDuplicateEntry", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Version control", + "index": 113, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JsonStandardCompliance", + "shortDescription": { + "text": "Compliance with JSON standard" + }, + "fullDescription": { + "text": "Reports the following discrepancies of a JSON file with the language specification: A line or block comment (configurable). Multiple top-level values (expect for JSON Lines files, configurable for others). A trailing comma in an object or array (configurable). A single quoted string. A property key is a not a double quoted strings. A NaN or Infinity/-Infinity numeric value as a floating point literal (configurable).", + "markdown": "Reports the following discrepancies of a JSON file with [the language specification](https://tools.ietf.org/html/rfc7159):\n\n* A line or block comment (configurable).\n* Multiple top-level values (expect for JSON Lines files, configurable for others).\n* A trailing comma in an object or array (configurable).\n* A single quoted string.\n* A property key is a not a double quoted strings.\n* A NaN or Infinity/-Infinity numeric value as a floating point literal (configurable)." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "JsonStandardCompliance", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "JSON and JSON5", + "index": 105, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JsonSchemaDeprecation", + "shortDescription": { + "text": "Deprecated JSON property" + }, + "fullDescription": { + "text": "Reports a deprecated property in a JSON file. Note that deprecation mechanism is not defined in the JSON Schema specification yet, and this inspection uses a non-standard extension 'deprecationMessage'.", + "markdown": "Reports a deprecated property in a JSON file. \nNote that deprecation mechanism is not defined in the JSON Schema specification yet, and this inspection uses a non-standard extension 'deprecationMessage'." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "JsonSchemaDeprecation", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "JSON and JSON5", + "index": 105, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JsonSchemaRefReference", + "shortDescription": { + "text": "Unresolved '$ref' and '$schema' references" + }, + "fullDescription": { + "text": "Reports an unresolved '$ref' or '$schema' path in a JSON schema.", + "markdown": "Reports an unresolved `$ref` or `$schema` path in a JSON schema. " + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "JsonSchemaRefReference", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JSON and JSON5", + "index": 105, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SSBasedInspection", + "shortDescription": { + "text": "Structural search inspection" + }, + "fullDescription": { + "text": "Allows configuring Structural Search/Structural Replace templates that you can apply to the file you are editing. All matches will be highlighted and marked with the template name that you have configured. If you configure the Structural Replace pattern as well, the corresponding replace option will be available as a quick-fix.", + "markdown": "Allows configuring **Structural Search/Structural Replace** templates that you can apply to the file you are editing.\n\nAll matches will be highlighted and marked with the template name that you have configured.\nIf you configure the **Structural Replace** pattern as well, the corresponding replace option will be available as a quick-fix." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SSBasedInspection", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Structural search", + "index": 117, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CheckValidXmlInScriptTagBody", + "shortDescription": { + "text": "Malformed content of 'script' tag" + }, + "fullDescription": { + "text": "Reports contents of 'script' tags that are invalid XML. Example: '' After the quick-fix is applied: ''", + "markdown": "Reports contents of `script` tags that are invalid XML. \n\n**Example:**\n\n\n \n\nAfter the quick-fix is applied:\n\n\n \n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "CheckValidXmlInScriptTagBody", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "HTML", + "index": 29, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RegExpSuspiciousBackref", + "shortDescription": { + "text": "Suspicious back reference" + }, + "fullDescription": { + "text": "Reports back references that will not be resolvable at runtime. This means that the back reference can never match anything. A back reference will not be resolvable when the group is defined after the back reference, or if the group is defined in a different branch of an alternation. Example of a group defined after its back reference: '\\1(abc)' Example of a group and a back reference in different branches: 'a(b)c|(xy)\\1z' New in 2022.1", + "markdown": "Reports back references that will not be resolvable at runtime. This means that the back reference can never match anything. A back reference will not be resolvable when the group is defined after the back reference, or if the group is defined in a different branch of an alternation.\n\n**Example of a group defined after its back reference:**\n\n\n \\1(abc)\n\n**Example of a group and a back reference in different branches:**\n\n\n a(b)c|(xy)\\1z\n\nNew in 2022.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "RegExpSuspiciousBackref", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "RegExp", + "index": 83, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RegExpSingleCharAlternation", + "shortDescription": { + "text": "Single character alternation" + }, + "fullDescription": { + "text": "Reports single char alternation in a RegExp. It is simpler to use a character class instead. This may also provide better matching performance. Example: 'a|b|c|d' After the quick-fix is applied: '[abcd]' New in 2017.1", + "markdown": "Reports single char alternation in a RegExp. It is simpler to use a character class instead. This may also provide better matching performance.\n\n**Example:**\n\n\n a|b|c|d\n\nAfter the quick-fix is applied:\n\n\n [abcd]\n\n\nNew in 2017.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "RegExpSingleCharAlternation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "RegExp", + "index": 83, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CheckTagEmptyBody", + "shortDescription": { + "text": "Empty element content" + }, + "fullDescription": { + "text": "Reports XML elements without contents. Example: '\n \n ' After the quick-fix is applied: '\n \n '", + "markdown": "Reports XML elements without contents.\n\n**Example:**\n\n\n \n \n \n\nAfter the quick-fix is applied:\n\n\n \n \n \n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CheckTagEmptyBody", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "XML", + "index": 65, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnresolvedReference", + "shortDescription": { + "text": "Unresolved reference" + }, + "fullDescription": { + "text": "Reports an unresolved reference to a named pattern ('define') in RELAX-NG files that use XML syntax. Suggests creating the referenced 'define' element.", + "markdown": "Reports an unresolved reference to a named pattern (`define`) in RELAX-NG files that use XML syntax. Suggests creating the referenced `define` element." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "UnresolvedReference", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "RELAX NG", + "index": 119, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "HtmlMissingClosingTag", + "shortDescription": { + "text": "Missing closing tag" + }, + "fullDescription": { + "text": "Reports an HTML element without a closing tag. Some coding styles require that HTML elements have closing tags even where this is optional. Example: '\n \n

Behold!\n \n ' After the quick-fix is applied: '\n \n

Behold!

\n \n '", + "markdown": "Reports an HTML element without a closing tag. Some coding styles require that HTML elements have closing tags even where this is optional.\n\n**Example:**\n\n\n \n \n

Behold!\n \n \n\nAfter the quick-fix is applied:\n\n\n \n \n

Behold!

\n \n \n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "HtmlMissingClosingTag", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "HTML", + "index": 29, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "HtmlUnknownAnchorTarget", + "shortDescription": { + "text": "Unresolved fragment in a link" + }, + "fullDescription": { + "text": "Reports an unresolved last part of an URL after the '#' sign.", + "markdown": "Reports an unresolved last part of an URL after the `#` sign." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "HtmlUnknownAnchorTarget", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "HTML", + "index": 29, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "XmlInvalidId", + "shortDescription": { + "text": "Unresolved 'id' reference" + }, + "fullDescription": { + "text": "Reports an unresolved 'id' reference in XML.", + "markdown": "Reports an unresolved `id` reference in XML." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "XmlInvalidId", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "XML", + "index": 65, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ReassignedToPlainText", + "shortDescription": { + "text": "Reassigned to plain text" + }, + "fullDescription": { + "text": "Reports files that were explicitly re-assigned to Plain Text File Type. This association is unnecessary because the platform auto-detects text files by content automatically. You can dismiss this warning by removing the file type association in Settings | Editor | File Types | Text.", + "markdown": "Reports files that were explicitly re-assigned to Plain Text File Type. This association is unnecessary because the platform auto-detects text files by content automatically.\n\nYou can dismiss this warning by removing the file type association\nin **Settings \\| Editor \\| File Types \\| Text**." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ReassignedToPlainText", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "General", + "index": 44, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Annotator", + "shortDescription": { + "text": "Annotator" + }, + "fullDescription": { + "text": "Reports issues essential to this file (e.g., syntax errors) in the result of a batch code inspection run. These issues are usually always highlighted in the editor and can't be configured, unlike inspections. These options control the scope of checks performed by this inspection: Option \"Report syntax errors\": report parser-related issues. Option \"Report issues from language-specific annotators\": report issues found by annotators configured for the relevant language. See Custom Language Support: Annotators for details. Option \"Report other highlighting problems\": report issues specific to the language of the current file (e.g., type mismatches or unreported exceptions). See Custom Language Support: Highlighting for details.", + "markdown": "Reports issues essential to this file (e.g., syntax errors) in the result of a batch code inspection run. These issues are usually always highlighted in the editor and can't be configured, unlike inspections. These options control the scope of checks performed by this inspection:\n\n* Option \"**Report syntax errors**\": report parser-related issues.\n* Option \"**Report issues from language-specific annotators** \": report issues found by annotators configured for the relevant language. See [Custom Language Support: Annotators](https://plugins.jetbrains.com/docs/intellij/annotator.html) for details.\n* Option \"**Report other highlighting problems** \": report issues specific to the language of the current file (e.g., type mismatches or unreported exceptions). See [Custom Language Support: Highlighting](https://plugins.jetbrains.com/docs/intellij/syntax-highlighting-and-error-highlighting.html#semantic-highlighting) for details." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "Annotator", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "General", + "index": 44, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JsonDuplicatePropertyKeys", + "shortDescription": { + "text": "Duplicate keys in object literals" + }, + "fullDescription": { + "text": "Reports a duplicate key in an object literal.", + "markdown": "Reports a duplicate key in an object literal." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "JsonDuplicatePropertyKeys", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "JSON and JSON5", + "index": 105, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "XmlDeprecatedElement", + "shortDescription": { + "text": "Deprecated symbol" + }, + "fullDescription": { + "text": "Reports a deprecated XML element or attribute. Symbols can be marked by XML comment or documentation tag with text 'deprecated'.", + "markdown": "Reports a deprecated XML element or attribute.\n\nSymbols can be marked by XML comment or documentation tag with text 'deprecated'." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "XmlDeprecatedElement", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "XML", + "index": 65, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RegExpRedundantNestedCharacterClass", + "shortDescription": { + "text": "Redundant nested character class" + }, + "fullDescription": { + "text": "Reports unnecessary nested character classes. Example: '[a-c[x-z]]' After the quick-fix is applied: '[a-cx-z]' New in 2020.2", + "markdown": "Reports unnecessary nested character classes.\n\n**Example:**\n\n\n [a-c[x-z]]\n\nAfter the quick-fix is applied:\n\n\n [a-cx-z]\n\nNew in 2020.2" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "RegExpRedundantNestedCharacterClass", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "RegExp", + "index": 83, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RegExpOctalEscape", + "shortDescription": { + "text": "Octal escape" + }, + "fullDescription": { + "text": "Reports octal escapes, which are easily confused with back references. Use hexadecimal escapes to avoid confusion. Example: '\\07' After the quick-fix is applied: '\\x07' New in 2017.1", + "markdown": "Reports octal escapes, which are easily confused with back references. Use hexadecimal escapes to avoid confusion.\n\n**Example:**\n\n\n \\07\n\nAfter the quick-fix is applied:\n\n\n \\x07\n\nNew in 2017.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "RegExpOctalEscape", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "RegExp", + "index": 83, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnusedDefine", + "shortDescription": { + "text": "Unused define" + }, + "fullDescription": { + "text": "Reports an unused named pattern ('define') in a RELAX-NG file (XML or Compact Syntax). 'define' elements that are used through an include in another file are ignored.", + "markdown": "Reports an unused named pattern (`define`) in a RELAX-NG file (XML or Compact Syntax). `define` elements that are used through an include in another file are ignored." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnusedDefine", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "RELAX NG", + "index": 119, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RegExpAnonymousGroup", + "shortDescription": { + "text": "Anonymous capturing group or numeric back reference" + }, + "fullDescription": { + "text": "Reports anonymous capturing groups and numeric back references in a RegExp. These are only reported when the RegExp dialect supports named group and named group references. Named groups and named back references improve code readability and are recommended to use instead. When a capture is not needed, matching can be more performant and use less memory by using a non-capturing group, i.e. '(?:xxx)' instead of '(xxx)'. Example: '(\\d\\d\\d\\d)\\1' A better regex pattern could look like this: '(?\\d\\d\\d\\d)\\k' New in 2017.2", + "markdown": "Reports anonymous capturing groups and numeric back references in a RegExp. These are only reported when the RegExp dialect supports named group and named group references. Named groups and named back references improve code readability and are recommended to use instead. When a capture is not needed, matching can be more performant and use less memory by using a non-capturing group, i.e. `(?:xxx)` instead of `(xxx)`.\n\n**Example:**\n\n\n (\\d\\d\\d\\d)\\1\n\nA better regex pattern could look like this:\n\n\n (?\\d\\d\\d\\d)\\k\n\nNew in 2017.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RegExpAnonymousGroup", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "RegExp", + "index": 83, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RegExpRepeatedSpace", + "shortDescription": { + "text": "Consecutive spaces" + }, + "fullDescription": { + "text": "Reports multiple consecutive spaces in a RegExp. Because spaces are not visible by default, it can be hard to see how many spaces are required. The RegExp can be made more clear by replacing the consecutive spaces with a single space and a counted quantifier. Example: '( )' After the quick-fix is applied: '( {5})' New in 2017.1", + "markdown": "Reports multiple consecutive spaces in a RegExp. Because spaces are not visible by default, it can be hard to see how many spaces are required. The RegExp can be made more clear by replacing the consecutive spaces with a single space and a counted quantifier.\n\n**Example:**\n\n\n ( )\n\nAfter the quick-fix is applied:\n\n\n ( {5})\n\n\nNew in 2017.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "RegExpRepeatedSpace", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "RegExp", + "index": 83, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RegExpDuplicateAlternationBranch", + "shortDescription": { + "text": "Duplicate branch in alternation" + }, + "fullDescription": { + "text": "Reports duplicate branches in a RegExp alternation. Duplicate branches slow down matching and obscure the intent of the expression. Example: '(alpha|bravo|charlie|alpha)' After the quick-fix is applied: '(alpha|bravo|charlie)' New in 2017.1", + "markdown": "Reports duplicate branches in a RegExp alternation. Duplicate branches slow down matching and obscure the intent of the expression.\n\n**Example:**\n\n\n (alpha|bravo|charlie|alpha)\n\nAfter the quick-fix is applied:\n\n\n (alpha|bravo|charlie)\n\nNew in 2017.1" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "RegExpDuplicateAlternationBranch", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "RegExp", + "index": 83, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CheckEmptyScriptTag", + "shortDescription": { + "text": "Empty tag" + }, + "fullDescription": { + "text": "Reports empty tags that do not work in some browsers. Example: '\n \n '", + "markdown": "Reports empty tags that do not work in some browsers.\n\n**Example:**\n\n\n \n \n \n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CheckEmptyScriptTag", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "HTML", + "index": 29, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + } + ], + "language": "en-US", + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "isComprehensive": false + }, + { + "name": "org.editorconfig.editorconfigjetbrains", + "version": "233.14714", + "rules": [ + { + "id": "EditorConfigRootDeclarationUniqueness", + "shortDescription": { + "text": "Extra top-level declaration" + }, + "fullDescription": { + "text": "Reports multiple top-level declarations. There can be only one optional “root=true” top-level declaration in the EditorConfig file. Using multiple top-level declarations is not allowed.", + "markdown": "Reports multiple top-level declarations. There can be only one optional \"root=true\" top-level declaration in the EditorConfig file. Using multiple top-level declarations is not allowed." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "EditorConfigRootDeclarationUniqueness", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigNumerousWildcards", + "shortDescription": { + "text": "Too many wildcards" + }, + "fullDescription": { + "text": "Reports sections that contain too many wildcards. Using a lot of wildcards may lead to performance issues.", + "markdown": "Reports sections that contain too many wildcards. Using a lot of wildcards may lead to performance issues." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "EditorConfigNumerousWildcards", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigKeyCorrectness", + "shortDescription": { + "text": "Unknown property" + }, + "fullDescription": { + "text": "Reports properties that are not supported by the IDE. Note: some “ij” domain properties may require specific language plugins.", + "markdown": "Reports properties that are not supported by the IDE. Note: some \"ij\" domain properties may require specific language plugins." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EditorConfigKeyCorrectness", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigEncoding", + "shortDescription": { + "text": "File encoding doesn't match EditorConfig charset" + }, + "fullDescription": { + "text": "Checks that current file encoding matches the encoding defined in \"charset\" property of .editorconfig file.", + "markdown": "Checks that current file encoding matches the encoding defined in \"charset\" property of .editorconfig file." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EditorConfigEncoding", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigRootDeclarationCorrectness", + "shortDescription": { + "text": "Unexpected top-level declaration" + }, + "fullDescription": { + "text": "Reports unexpected top-level declarations. Top-level declarations other than “root=true” are not allowed in the EditorConfig file.", + "markdown": "Reports unexpected top-level declarations. Top-level declarations other than \"root=true\" are not allowed in the EditorConfig file." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "EditorConfigRootDeclarationCorrectness", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigPatternRedundancy", + "shortDescription": { + "text": "Duplicate or redundant pattern" + }, + "fullDescription": { + "text": "Reports file patterns that are redundant as there already are other patterns that define the same scope of files or even a broader one. For example, in '[{*.java,*}]' the first '*.java' pattern defines a narrower scope compared to '*'. That is why it is redundant and can be removed.", + "markdown": "Reports file patterns that are redundant as there already are other patterns that define the same scope of files or even a broader one. For example, in `[{*.java,*}]` the first `*.java` pattern defines a narrower scope compared to `*`. That is why it is redundant and can be removed." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EditorConfigPatternRedundancy", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigDeprecatedDescriptor", + "shortDescription": { + "text": "Deprecated property" + }, + "fullDescription": { + "text": "Reports EditorConfig properties that are no longer supported.", + "markdown": "Reports EditorConfig properties that are no longer supported." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EditorConfigDeprecatedDescriptor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigShadowedOption", + "shortDescription": { + "text": "Overridden property" + }, + "fullDescription": { + "text": "Reports properties that are already defined in other sections. For example: '[*.java]\nindent_size=4\n[{*.java,*.js}]\nindent_size=2' The second section includes all '*.java' files too but it also redefines indent_size. As a result the value 2 will be used for files matching '*.java'.", + "markdown": "Reports properties that are already defined in other sections.\n\nFor example:\n\n\n [*.java]\n indent_size=4\n [{*.java,*.js}]\n indent_size=2\n\nThe second section includes all `*.java` files too but it also redefines indent_size. As a result the value 2 will be used for files matching `*.java`." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EditorConfigShadowedOption", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigEmptyHeader", + "shortDescription": { + "text": "Empty header" + }, + "fullDescription": { + "text": "Reports sections with an empty header. Section header must contain file path globs in the format similar to one supported by 'gitignore'.", + "markdown": "Reports sections with an empty header. Section header must contain file path globs in the format similar to one supported by `gitignore`." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "EditorConfigEmptyHeader", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigValueUniqueness", + "shortDescription": { + "text": "Non-unique list value" + }, + "fullDescription": { + "text": "Reports duplicates in lists of values.", + "markdown": "Reports duplicates in lists of values." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "EditorConfigValueUniqueness", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigMissingRequiredDeclaration", + "shortDescription": { + "text": "Required declarations are missing" + }, + "fullDescription": { + "text": "Reports properties that miss the required declarations. Refer to the documentation for more information.", + "markdown": "Reports properties that miss the required declarations. Refer to the documentation for more information." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "EditorConfigMissingRequiredDeclaration", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigSpaceInHeader", + "shortDescription": { + "text": "Space in file pattern" + }, + "fullDescription": { + "text": "Reports space characters in wildcard patterns that affect pattern matching. If these characters are not intentional, they should be removed.", + "markdown": "Reports space characters in wildcard patterns that affect pattern matching. If these characters are not intentional, they should be removed." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "EditorConfigSpaceInHeader", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigOptionRedundancy", + "shortDescription": { + "text": "Redundant property" + }, + "fullDescription": { + "text": "Reports properties that are redundant when another applicable section already contains the same property and value. For example: '[*]\nindent_size=4\n[*.java]\nindent_size=4' are both applicable to '*.java' files and define the same 'indent_size' value.", + "markdown": "Reports properties that are redundant when another applicable section already contains the same property and value.\n\n\nFor example:\n\n\n [*]\n indent_size=4\n [*.java]\n indent_size=4\n\nare both applicable to `*.java` files and define the same `indent_size` value." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EditorConfigOptionRedundancy", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigUnexpectedComma", + "shortDescription": { + "text": "Unexpected comma" + }, + "fullDescription": { + "text": "Reports commas that cannot be used in the current context. Commas are allowed only as separators for values in lists.", + "markdown": "Reports commas that cannot be used in the current context. Commas are allowed only as separators for values in lists." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "EditorConfigUnexpectedComma", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigCharClassRedundancy", + "shortDescription": { + "text": "Unnecessary character class" + }, + "fullDescription": { + "text": "Reports character classes that consist of a single character. Such classes can be simplified to a character, for example '[a]'→'a'.", + "markdown": "Reports character classes that consist of a single character. Such classes can be simplified to a character, for example `[a]`→`a`." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EditorConfigCharClassRedundancy", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigPartialOverride", + "shortDescription": { + "text": "Overlapping sections" + }, + "fullDescription": { + "text": "Reports subsets of files specified in the current section that overlap with other subsets in other sections. For example: '[{foo,bar}]' and '[{foo,bas}]' both contain “foo”.", + "markdown": "Reports subsets of files specified in the current section that overlap with other subsets in other sections. For example: `[{foo,bar}]` and `[{foo,bas}]` both contain \"foo\"." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "EditorConfigPartialOverride", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigEmptySection", + "shortDescription": { + "text": "Empty section" + }, + "fullDescription": { + "text": "Reports sections that do not contain any EditorConfig properties.", + "markdown": "Reports sections that do not contain any EditorConfig properties." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EditorConfigEmptySection", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigShadowingOption", + "shortDescription": { + "text": "Overriding property" + }, + "fullDescription": { + "text": "Reports properties that override the same properties defined earlier in the file. For example: '[*.java]\nindent_size=4\n[{*.java,*.js}]\nindent_size=2' The second section includes the same files as '[*.java]' but also sets indent_size to value 2. Thus the first declaration 'indent_size=4'will be ignored.", + "markdown": "Reports properties that override the same properties defined earlier in the file.\n\nFor example:\n\n\n [*.java]\n indent_size=4\n [{*.java,*.js}]\n indent_size=2\n\nThe second section includes the same files as `[*.java]` but also sets indent_size to value 2. Thus the first declaration `indent_size=4`will be ignored." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EditorConfigShadowingOption", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigListAcceptability", + "shortDescription": { + "text": "Unexpected value list" + }, + "fullDescription": { + "text": "Reports lists of values that are used in properties in which lists are not supported. In this case, only a single value can be specified.", + "markdown": "Reports lists of values that are used in properties in which lists are not supported. In this case, only a single value can be specified." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "EditorConfigListAcceptability", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigPatternEnumerationRedundancy", + "shortDescription": { + "text": "Unnecessary braces" + }, + "fullDescription": { + "text": "Reports pattern lists that are either empty '{}' or contain just one pattern, for example '{foo}' in contrast to a list containing multiple patterns, for example '{foo,bar}'. In this case braces are handled as a part of the name. For example, the pattern '*.{a}' will match the file 'my.{a}' but not 'my.a'.", + "markdown": "Reports pattern lists that are either empty `{}` or contain just one pattern, for example `{foo}` in contrast to a list containing multiple patterns, for example `{foo,bar}`. In this case braces are handled as a part of the name. For example, the pattern `*.{a}` will match the file `my.{a}` but not `my.a`." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "EditorConfigPatternEnumerationRedundancy", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigReferenceCorrectness", + "shortDescription": { + "text": "Invalid reference" + }, + "fullDescription": { + "text": "Reports identifiers that are either unknown or have a wrong type.", + "markdown": "Reports identifiers that are either unknown or have a wrong type." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "EditorConfigReferenceCorrectness", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigPairAcceptability", + "shortDescription": { + "text": "Unexpected key-value pair" + }, + "fullDescription": { + "text": "Reports key-value pairs that are not allowed in the current context.", + "markdown": "Reports key-value pairs that are not allowed in the current context." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "EditorConfigPairAcceptability", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigNoMatchingFiles", + "shortDescription": { + "text": "No matching files" + }, + "fullDescription": { + "text": "Reports sections with wildcard patterns that do not match any files under the directory in which the '.editorconfig' file is located.", + "markdown": "Reports sections with wildcard patterns that do not match any files under the directory in which the `.editorconfig` file is located." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EditorConfigNoMatchingFiles", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigWildcardRedundancy", + "shortDescription": { + "text": "Redundant wildcard" + }, + "fullDescription": { + "text": "Reports wildcards that become redundant when the “**” wildcard is used in the same section. The “**” wildcard defines a broader set of files than any other wildcard. That is why, any other wildcard used in the same section has no affect and can be removed.", + "markdown": "Reports wildcards that become redundant when the \"\\*\\*\" wildcard is used in the same section.\n\n\nThe \"\\*\\*\" wildcard defines a broader set of files than any other wildcard.\nThat is why, any other wildcard used in the same section has no affect and can be removed." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EditorConfigWildcardRedundancy", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigHeaderUniqueness", + "shortDescription": { + "text": "EditorConfig section is not unique" + }, + "fullDescription": { + "text": "Reports sections that define the same file pattern as other sections.", + "markdown": "Reports sections that define the same file pattern as other sections." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EditorConfigHeaderUniqueness", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigValueCorrectness", + "shortDescription": { + "text": "Invalid property value" + }, + "fullDescription": { + "text": "Reports property values that do not meet value restrictions. For example, some properties may be only “true” or “false”, others contain only integer numbers etc. If a value has a limited set of variants, use code completion to see all of them.", + "markdown": "Reports property values that do not meet value restrictions. For example, some properties may be only \"true\" or \"false\", others contain only integer numbers etc. If a value has a limited set of variants, use code completion to see all of them." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "EditorConfigValueCorrectness", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigVerifyByCore", + "shortDescription": { + "text": "Invalid .editorconfig file" + }, + "fullDescription": { + "text": "Verifies the whole file using the backing EditorConfig core library and reports any failures. Any such failure would prevent EditorConfig properties from being correctly applied.", + "markdown": "Verifies the whole file using the backing EditorConfig core library and reports any failures. Any such failure would prevent EditorConfig properties from being correctly applied." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "EditorConfigVerifyByCore", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigCharClassLetterRedundancy", + "shortDescription": { + "text": "Duplicate character class letter" + }, + "fullDescription": { + "text": "Reports wildcard patterns in the EditorConfig section that contain a duplicate character in the character class, for example '[aa]'.", + "markdown": "Reports wildcard patterns in the EditorConfig section that contain a duplicate character in the character class, for example `[aa]`." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EditorConfigCharClassLetterRedundancy", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "EditorConfigUnusedDeclaration", + "shortDescription": { + "text": "Unused declaration" + }, + "fullDescription": { + "text": "Reports unused declarations. Such declarations can be removed.", + "markdown": "Reports unused declarations. Such declarations can be removed." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "EditorConfigUnusedDeclaration", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "EditorConfig", + "index": 31, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + } + ], + "language": "en-US", + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "isComprehensive": false + }, + { + "name": "com.intellij.java-i18n", + "version": "233.14714", + "rules": [ + { + "id": "UnusedMessageFormatParameter", + "shortDescription": { + "text": "Missing message format parameter" + }, + "fullDescription": { + "text": "Reports properties values that look like 'java.text.MessageFormat' format strings but do not use some the parameters of the '{xx}' kind. Example: '# parameter {0} is not used\nerror.message=Something happened in line {1}'", + "markdown": "Reports properties values that look like `java.text.MessageFormat` format strings but do not use some the parameters of the `{xx}` kind.\n\nExample:\n\n\n # parameter {0} is not used\n error.message=Something happened in line {1}\n \n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnusedMessageFormatParameter", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Properties files", + "index": 32, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConvertToBasicLatin", + "shortDescription": { + "text": "Non-Basic Latin character" + }, + "fullDescription": { + "text": "Reports non-Basic Latin characters in literals and suggests replacing them with unicode entities. Example: '// © 2021\n char c = '©';\n String s = \"Áî\";'\n After the quick-fix is applied: '// © 2021\n char c = '\\u00a9';\n String s = \"\\u00c1\\u00ee\";'", + "markdown": "Reports non-Basic Latin characters in literals and suggests replacing them with unicode entities.\n\nExample:\n\n\n // © 2021\n char c = '©';\n String s = \"Áî\";\n\nAfter the quick-fix is applied:\n\n\n // © 2021\n char c = '\\u00a9';\n String s = \"\\u00c1\\u00ee\";\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ConvertToBasicLatin", + "ideaSeverity": "INFORMATION", + "qodanaSeverity": "Info" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Internationalization", + "index": 9, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InconsistentResourceBundle", + "shortDescription": { + "text": "Inconsistent resource bundle" + }, + "fullDescription": { + "text": "Reports problems in the properties files contained in the resource bundle. Report missing translations Use this option to report properties contained in the parent properties file that are missing in inherited ones (unless it's a language dialect). Example: '# messages.properties\n abc=xxx\n\n # messages_fr.properties\n # Empty file' Property 'abc' will be reported as untranslated. Report inconsistent properties Use this option to report properties contained in inherited properties file that are missing in the parent one (or in siblings if there is no parent). Example: '# messages.properties\n # Empty file\n\n # messages_fr.properties\n abc=xxx' Property 'abc' translation is not available here for any language except French, and, thus, will be reported as missing in the (default) properties file 'messages.properties'. Report properties overridden with the same value Use this option to report properties copy-pasted into several properties files verbatim. Example: '# messages.properties\n abc=xxx\n\n # messages_fr.properties\n abc=xxx' Property 'abc' will be reported as unnecessarily inherited in the file 'messages_fr.properties' . Report properties overridden with different placeholders Use this option to check for placeholder consistency in overridden properties. Example: '# messages.properties\n qwe={0}xxx{1}\n abc={0}yyy{1}\n\n # messages_fr.properties\n qwe={0}xxx{0}xxx{1}\n abc={0}yyy' Property 'abc' will be reported as a property containing message format placeholders not corresponding to 'messages.properties'. Report properties overridden with different values endings Use this option to check for ending consistency in overridden properties. Example: '# messages.properties\n abc=xxxzzz\n\n # messages_fr.properties\n abc=xxx;' Property 'abc' will be reported as ending with special signs ('!' / '?' / '.' / ':' / ';') whereas the parent value in 'messages.properties' doesn't.", + "markdown": "Reports problems in the properties files contained in the resource bundle.\n\n* **Report missing translations** \n\n Use this option to report properties contained in the parent properties file that are missing in inherited ones (unless it's a language dialect). \n\n Example:\n\n\n # messages.properties\n abc=xxx\n\n # messages_fr.properties\n # Empty file\n \n Property `abc` will be reported as untranslated. \n\n* **Report inconsistent properties** \n\n Use this option to report properties contained in inherited properties file that are missing in the parent one (or in siblings if there is no parent). \n\n Example:\n\n\n # messages.properties\n # Empty file\n\n # messages_fr.properties\n abc=xxx\n \n Property `abc` translation is not available here for any language except French, and, thus, will be reported as missing in the (default) properties file `messages.properties`. \n\n* **Report properties overridden with the same value** \n\n Use this option to report properties copy-pasted into several properties files verbatim. \n\n Example:\n\n\n # messages.properties\n abc=xxx\n\n # messages_fr.properties\n abc=xxx\n \n Property `abc` will be reported as unnecessarily inherited in the file `messages_fr.properties` . \n\n* **Report properties overridden with different placeholders** \n\n Use this option to check for placeholder consistency in overridden properties. \n\n Example:\n\n\n # messages.properties\n qwe={0}xxx{1}\n abc={0}yyy{1}\n\n # messages_fr.properties\n qwe={0}xxx{0}xxx{1}\n abc={0}yyy\n \n Property `abc` will be reported as a property containing message format placeholders not corresponding to `messages.properties`. \n\n* **Report properties overridden with different values endings** \n\n Use this option to check for ending consistency in overridden properties. \n\n Example:\n\n\n # messages.properties\n abc=xxxzzz\n\n # messages_fr.properties\n abc=xxx;\n \n Property `abc` will be reported as ending with special signs (`!` / `?` / `.` / `:` / `;`) whereas the parent value in `messages.properties` doesn't." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "InconsistentResourceBundle", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Properties files", + "index": 32, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "HardCodedStringLiteral", + "shortDescription": { + "text": "Hardcoded strings" + }, + "fullDescription": { + "text": "Reports any instances of hardcoded 'String' literals. Hardcoded 'String' literals are probably errors in an internationalized environment. This inspection won't report empty strings and strings consisting only of whitespaces. A quick-fix is available to transform a string literal into a 'java.util.ResourceBundle.getString()' method call.", + "markdown": "Reports any instances of hardcoded `String` literals.\n\nHardcoded `String` literals are probably errors in an\ninternationalized environment. This inspection won't report empty strings and strings consisting only of whitespaces. A quick-fix is available\nto transform a string literal into a `java.util.ResourceBundle.getString()` method call." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "HardCodedStringLiteral", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Internationalization", + "index": 9, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DialogTitleCapitalization", + "shortDescription": { + "text": "Incorrect string capitalization" + }, + "fullDescription": { + "text": "Reports strings in method parameters and return values annotated with '@Nls' and having the capitalization parameter to conform to capitalization rules existing in most platform UI guidelines. Example: 'void setTitle(@NlsContexts.DialogTitle String title) {}\n setTitle(\"This is sentence capitalization but should be title\");' After the quick-fix is applied: 'setTitle(\"This Is Sentence Capitalization but Should Be Title\");'", + "markdown": "Reports strings in method parameters and return values annotated with `@Nls` and having the capitalization parameter to conform to capitalization rules existing in most platform UI guidelines.\n\n**Example:**\n\n\n void setTitle(@NlsContexts.DialogTitle String title) {}\n setTitle(\"This is sentence capitalization but should be title\"); \n\nAfter the quick-fix is applied:\n\n\n setTitle(\"This Is Sentence Capitalization but Should Be Title\"); \n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "DialogTitleCapitalization", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Internationalization", + "index": 9, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnresolvedPropertyKey", + "shortDescription": { + "text": "Invalid property key" + }, + "fullDescription": { + "text": "Reports invalid arguments that are passed to methods with parameters annotated as '@PropertyKey'. These arguments should be valid property keys in corresponding properties files. Also, the inspection verifies that the 'resourceBundle' argument of the '@PropertyKey' annotation is an existing resource bundle. Use the quick-fix to create a new property or to select an existing one. Example: '@PropertyKey(resourceBundle = \"myBundle\") String value = \"invalid.key\";'", + "markdown": "Reports invalid arguments that are passed to methods with parameters annotated as `@PropertyKey`.\n\nThese arguments should be valid property keys in corresponding properties files.\nAlso, the inspection verifies that the `resourceBundle`\nargument of the `@PropertyKey` annotation is an existing resource bundle.\n\n\nUse the quick-fix to create a new property or to select an existing one.\n\nExample:\n\n\n @PropertyKey(resourceBundle = \"myBundle\") String value = \"invalid.key\";\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "error", + "parameters": { + "suppressToolId": "UnresolvedPropertyKey", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Properties files", + "index": 120, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SuspiciousLocalesLanguages", + "shortDescription": { + "text": "Suspicious resource bundle locale languages" + }, + "fullDescription": { + "text": "Reports locales with language codes that are not supported by Java.", + "markdown": "Reports locales with language codes that are not supported by Java." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SuspiciousLocalesLanguages", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Properties files", + "index": 32, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DuplicateStringLiteralInspection", + "shortDescription": { + "text": "Duplicate string literal" + }, + "fullDescription": { + "text": "Reports string literals that are replicated unchanged throughout the project. Two quick-fixes are provided. One to introduce a constant for a duplicated string and use it throughout the project, and one to show the location of all the duplicates of a particular string literal. Example: 'class C1 { String CONST1 = \"duplicate string\"; }\n class C2 { String CONST2 = \"duplicate string\"; }' Configure the inspection: Use the Min string length field to set the minimal string length required to detect duplicates. Use the Ignore @PropertyKey expressions option to ignore strings passed as arguments to methods annotated with 'org.jetbrains.annotations.PropertyKey'.", + "markdown": "Reports string literals that are replicated unchanged throughout the project. Two quick-fixes are provided. One to introduce a constant for a duplicated string and use it throughout the project, and one to show the location of all the duplicates of a particular string literal.\n\nExample:\n\n\n class C1 { String CONST1 = \"duplicate string\"; }\n class C2 { String CONST2 = \"duplicate string\"; }\n\nConfigure the inspection:\n\n* Use the **Min string length** field to set the minimal string length required to detect duplicates.\n* Use the **Ignore @PropertyKey expressions** option to ignore strings passed as arguments to methods annotated with `org.jetbrains.annotations.PropertyKey`." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "DuplicateStringLiteralInspection", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Internationalization", + "index": 9, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + } + ], + "language": "en-US", + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "isComprehensive": false + }, + { + "name": "org.jetbrains.plugins.gradle", + "version": "233.14714", + "rules": [ + { + "id": "ForeignDelegate", + "shortDescription": { + "text": "Possibly misplaced call to Gradle method" + }, + "fullDescription": { + "text": "Detects possibly misplaced calls to Gradle methods. Gradle buildscripts comprise a lot of nested closures, making the code structure similar to a markup language. Due to the behavior of DSL languages, the methods that you can write in the outer closures are also available in the inner ones. Such methods may have no meaning when written outside their scope. Sometimes it may be hard to detect this situation. This inspection aims to detect such methods. Example: 'repositories {\n // the delegate of 'repositories' has method 'exclusiveContent', which can be written here\n maven {\n // the delegate of 'maven' has method 'content', which can be written here\n // but 'exclusiveContent' is also available\n exclusiveContent {} // reports 'exclusiveContent'\n }\n}'", + "markdown": "Detects possibly misplaced calls to Gradle methods.\nGradle buildscripts comprise a lot of nested closures, making the code structure similar to a markup language. Due to the behavior of DSL languages, the methods that you can write in the outer closures are also available in the inner ones. Such methods may have no meaning when written outside their scope. \nSometimes it may be hard to detect this situation. This inspection aims to detect such methods.\n\n**Example:**\n\n\n repositories {\n // the delegate of 'repositories' has method 'exclusiveContent', which can be written here\n maven {\n // the delegate of 'maven' has method 'content', which can be written here\n // but 'exclusiveContent' is also available\n exclusiveContent {} // reports 'exclusiveContent'\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "ForeignDelegate", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Gradle/Probable bugs", + "index": 40, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IncorrectPluginDslStructure", + "shortDescription": { + "text": "Plugin DSL structure" + }, + "fullDescription": { + "text": "Detects disallowed statements before 'plugins {}' block. Due to the limitations of Gradle Plugin DSL, only a restricted set of Groovy statements is available before ''plugins {}'' block. The only options are ''buildscript {}'', ''pluginManagement {}'' and other ''plugins {}''. See Gradle documentation Example: 'import foo.bar.Baz\nplugins {} // reports 'plugins'\nplugins {\n foo() // reports 'foo'\n id 'java'\n}'", + "markdown": "Detects disallowed statements before 'plugins {}' block.\nDue to the limitations of Gradle Plugin DSL, only a restricted set of Groovy statements is available before '`plugins {}`' block. The only options are '`buildscript {}`', '`pluginManagement {}`' and other '`plugins {}`'. \n[See Gradle documentation](https://docs.gradle.org/current/userguide/plugins.html#plugins_dsl_limitations)\n\n**Example:**\n\n\n import foo.bar.Baz\n plugins {} // reports 'plugins'\n plugins {\n foo() // reports 'foo'\n id 'java'\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "IncorrectPluginDslStructure", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Gradle/Validity issues", + "index": 48, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BintrayPublishingPlugin", + "shortDescription": { + "text": "Bintray publishing plugin may stop working on May 1st, 2021" + }, + "fullDescription": { + "text": "Detects usages of Gradle plugin 'com.jfrog.bintray'. The plugin is used for publishing build results to Bintray. Publishing to Bintray service is disabled.", + "markdown": "Detects usages of Gradle plugin `com.jfrog.bintray`.\nThe plugin is used for publishing build results to Bintray.\nPublishing to\n[Bintray](https://www.jfrog.com/confluence/display/BT/Welcome+to+JFrog+Bintray) service is disabled." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "BintrayPublishingPlugin", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Gradle/Probable bugs", + "index": 40, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DependencyNotationArgument", + "shortDescription": { + "text": "Unrecognized dependency notation" + }, + "fullDescription": { + "text": "Detects incorrect dependency notations. The following types/formats are supported by Gradle: Instances of 'Dependency'; 'String' or 'CharSequence' values, for example ''org.gradle:gradle-core:1.0''; Maps, for example '[group: 'org.gradle', name: 'gradle-core', version: '1.0']'; FileCollections, for example 'files('some.jar', 'someOther.jar')'; Projects, for example 'project(':some:project:path')'; 'ClassPathNotation', for example 'gradleApi()'; Lists of dependency notations, for example '['org.gradle:gradle-core:1.0']'; (Gradle 7.0+) Version catalog accessors, for example 'libs.groovy.core'. See Gradle documentation Example: 'dependencies {\n implementation(1) // reports '1'\n}'", + "markdown": "Detects incorrect dependency notations.\nThe following types/formats are supported by Gradle:\n\n* Instances of `Dependency`;\n* `String` or `CharSequence` values, for example `'org.gradle:gradle-core:1.0'`;\n* Maps, for example `[group: 'org.gradle', name: 'gradle-core', version: '1.0']`;\n* FileCollections, for example `files('some.jar', 'someOther.jar')`;\n* Projects, for example `project(':some:project:path')`;\n* `ClassPathNotation`, for example `gradleApi()`;\n* Lists of dependency notations, for example `['org.gradle:gradle-core:1.0']`;\n* (Gradle 7.0+) [Version catalog accessors](https://docs.gradle.org/current/userguide/platforms.html), for example `libs.groovy.core`.\n\n[See Gradle documentation](https://docs.gradle.org/current/userguide/declaring_dependencies.html#sec:dependency-types)\n\n**Example:**\n\n\n dependencies {\n implementation(1) // reports '1'\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "DependencyNotationArgument", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Gradle/Probable bugs", + "index": 40, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DeprecatedConfigurations", + "shortDescription": { + "text": "Deprecated configurations" + }, + "fullDescription": { + "text": "Detects usage of configuration methods that were deprecated. Configuration methods may be deprecated because of two possible reasons: The Gradle API has evolved, so old method choices should be avoided; Some plugin provides a better version of the deprecated configuration method. Most likely there will be an alternative for the deprecated method. Example: 'plugins {\n id 'java'\n}\n\ndependencies {\n archive 'org.gradle.api:gradle:1.0' // reports 'archive'\n}'", + "markdown": "Detects usage of configuration methods that were deprecated.\nConfiguration methods may be deprecated because of two possible reasons:\n\n* The Gradle API has evolved, so old method choices should be avoided;\n* Some plugin provides a better version of the deprecated configuration method.\n\nMost likely there will be an alternative for the deprecated method.\n\n**Example:**\n\n\n plugins {\n id 'java'\n }\n\n dependencies {\n archive 'org.gradle.api:gradle:1.0' // reports 'archive'\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "DeprecatedConfigurations", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Gradle/Validity issues", + "index": 48, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MultipleRepositoryUrls", + "shortDescription": { + "text": "Multiple repository urls" + }, + "fullDescription": { + "text": "Reports the usage of multiple URLs per repository (maven or ivy) block. The problem is that only one URL can be picked up for the repository, the other URLs will be ignored.", + "markdown": "Reports the usage of multiple URLs per repository (maven or ivy) block. The problem is that only one URL can be picked up for the repository, the other URLs will be ignored." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MultipleRepositoryUrls", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Gradle/Probable bugs", + "index": 40, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JCenterRepository", + "shortDescription": { + "text": "Builds will no longer be able to resolve artifacts from JCenter after February 1st, 2022" + }, + "fullDescription": { + "text": "Detects usages of the JCenter repository to resolve dependencies. Builds will no longer be able to resolve artifacts from JCenter after February 1st, 2022.", + "markdown": "Detects usages of the JCenter repository to resolve dependencies. Builds will no longer be able to resolve artifacts from JCenter after February 1st, 2022." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "JCenterRepository", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Gradle/Probable bugs", + "index": 40, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConfigurationAvoidance", + "shortDescription": { + "text": "Configuration avoidance" + }, + "fullDescription": { + "text": "(Gradle 4.9+) Detects usage of API that interacts with tasks eagerly. Eager interaction with tasks implies some inconveniences: The user should manually set up all dependencies between tasks; In the configuration phase, all the tasks accessed via the eager API become configured, even if they are not executed afterwards. It results in performance degradation. Eventually, the eager API will be deprecated in favor of the lazy one. For a migration guide, see the Gradle documentation. Example: 'task foo { // reports 'task', suggests replacing it with 'task.register'\n // ...\n}'", + "markdown": "(Gradle 4.9+) Detects usage of API that interacts with tasks eagerly.\n\nEager interaction with tasks implies some inconveniences:\n\n* The user should manually set up all dependencies between tasks;\n* In the [configuration phase](https://docs.gradle.org/current/userguide/build_lifecycle.html#sec:build_phases), all the tasks accessed via the eager API become configured, even if they are not executed afterwards. It results in performance degradation.\n\nEventually, the eager API will be deprecated in favor of the lazy one.\n\nFor a migration guide, see the\n[Gradle documentation](https://docs.gradle.org/current/userguide/task_configuration_avoidance.html).\n\n**Example:**\n\n task foo { // reports 'task', suggests replacing it with 'task.register'\n // ...\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ConfigurationAvoidance", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Gradle/Best practises", + "index": 125, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + } + ], + "language": "en-US", + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "isComprehensive": false + }, + { + "name": "DevKit", + "version": "233.14714", + "rules": [ + { + "id": "ExtensionClassShouldBeFinalAndNonPublic", + "shortDescription": { + "text": "Extension class should be final and non-public" + }, + "fullDescription": { + "text": "Reports extension classes that are non-final or public. New in 2023.2", + "markdown": "Reports extension classes that are non-final or public.\n\nNew in 2023.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ExtensionClassShouldBeFinalAndNonPublic", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "WorkspaceEntityMutableField", + "shortDescription": { + "text": "Unsupported 'var' field in entity" + }, + "fullDescription": { + "text": "Detects unsupported 'var' fields in the inheritors of 'WorkspaceEntity' interface Interface implementing 'WorkspaceEntity' have to have only 'val' fields because it's immutable. Implementation of 'WorkspaceEntity.Builder' will be generated for the mutation", + "markdown": "Detects unsupported `var` fields in the inheritors of `WorkspaceEntity` interface\n\n\nInterface implementing `WorkspaceEntity` have to have only `val` fields because it's immutable.\nImplementation of `WorkspaceEntity.Builder` will be generated for the mutation" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "WorkspaceEntityMutableField", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Workspace model", + "index": 56, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MigrateToOptControl", + "shortDescription": { + "text": "Migrate to getOptionPane()" + }, + "fullDescription": { + "text": "Reports 'createOptionsPanel()' methods in inspection implementation, which can be automatically converted to 'getOptionsPane()'. Creating inspection options control via 'createOptionsPanel()' is deprecated, in favor of declarative control description 'getOptionsPane()'. The inspection checks 'createOptionsPanel()' implementations and if they use InspectionOptionsPanel or its descendants and are simple enough, then it suggests to convert to the new API automatically. This inspection currently supports Java and Kotlin only. It cannot convert the code that uses Kotlin methods like 'apply' or 'also'. Try to inline them. New in 2023.1", + "markdown": "Reports `createOptionsPanel()` methods in inspection implementation, which can be automatically converted to `getOptionsPane()`.\n\n\nCreating inspection options control via `createOptionsPanel()` is deprecated,\nin favor of declarative control description `getOptionsPane()`.\nThe inspection checks `createOptionsPanel()` implementations and if they use\nInspectionOptionsPanel or its descendants and are simple enough, then it suggests to convert to\nthe new API automatically.\n\n\nThis inspection currently supports Java and Kotlin only.\nIt cannot convert the code that uses Kotlin methods like `apply` or `also`. Try to inline them.\n\nNew in 2023.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MigrateToOptControl", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PluginXmlCapitalization", + "shortDescription": { + "text": "Plugin.xml text capitalization" + }, + "fullDescription": { + "text": "Reports text capitalization problems in 'plugin.xml'. The following elements are checked: '' ', ' ', ' extension point properties annotated with 'org.jetbrains.annotations.Nls' specifying required 'capitalization' Please see Capitalization in IntelliJ Platform UI Guidelines for more information.", + "markdown": "Reports text capitalization problems in `plugin.xml`.\n\n\nThe following elements are checked:\n\n* ``\n* `, `\n* `, `\n* extension point properties annotated with `org.jetbrains.annotations.Nls` specifying required `capitalization`\n\n\nPlease see [Capitalization](https://jetbrains.design/intellij/text/capitalization/) in IntelliJ Platform UI Guidelines for more\ninformation." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PluginXmlCapitalization", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Plugin descriptor", + "index": 76, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "WorkspaceImplObsolete", + "shortDescription": { + "text": "Obsolete version of entity implementation" + }, + "fullDescription": { + "text": "Reports existence of the obsolete implementation for the entity. Verifies that existing implementation for entities has the same API version as described at 'com.intellij.platform.workspace.storage.CodeGeneratorVersions' from dependencies. Suggests regenerating implementation for the whole entities in the current module.", + "markdown": "Reports existence of the obsolete implementation for the entity.\n\n\nVerifies that existing implementation for entities has the same API version as described at `com.intellij.platform.workspace.storage.CodeGeneratorVersions` from dependencies.\n\n\nSuggests regenerating implementation for the whole entities in the current module." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "WorkspaceImplObsolete", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Workspace model", + "index": 56, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UseDPIAwareBorders", + "shortDescription": { + "text": "Use DPI-aware borders" + }, + "fullDescription": { + "text": "Reports usages of 'javax.swing.border.EmptyBorder' and 'JBUI.Borders.emptyXyz()' that can be simplified. The 'EmptyBorder' instances are not DPI-aware and can result in UI layout problems. Quick fix performs replacement with 'JBUI.Borders.empty()' or simplifies the expression. Example: '// bad:\nBorder border1 = new EmptyBorder(1, 2, 3, 4);\nBorder border2 = new EmptyBorder(1, 2, 1, 2);\nBorder border3 = new EmptyBorder(1, 0, 0, 0);\n\n// good:\nBorder border1 = JBUI.Borders.empty(1, 2, 3, 4);\nBorder border2 = JBUI.Borders.empty(1, 2);\nBorder border3 = JBUI.Borders.emptyTop(1);'", + "markdown": "Reports usages of `javax.swing.border.EmptyBorder` and `JBUI.Borders.emptyXyz()` that can be simplified.\n\n\nThe `EmptyBorder` instances are not DPI-aware and can result in UI layout problems.\n\n\nQuick fix performs replacement with `JBUI.Borders.empty()` or simplifies the expression.\n\nExample:\n\n\n // bad:\n Border border1 = new EmptyBorder(1, 2, 3, 4);\n Border border2 = new EmptyBorder(1, 2, 1, 2);\n Border border3 = new EmptyBorder(1, 0, 0, 0);\n\n // good:\n Border border1 = JBUI.Borders.empty(1, 2, 3, 4);\n Border border2 = JBUI.Borders.empty(1, 2);\n Border border3 = JBUI.Borders.emptyTop(1);\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UseDPIAwareBorders", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ExtensionRegisteredAsServiceOrComponent", + "shortDescription": { + "text": "Extension registered as service/component" + }, + "fullDescription": { + "text": "Reports extension implementation being additionally registered as a service/component. While there can be multiple extension instances, the IntelliJ Platform ensures that only one instance of a service/component is loaded, thus registering the same class as both an extension and a service/component may cause issues. New in 2023.2", + "markdown": "Reports extension implementation being additionally registered as a service/component.\n\n\nWhile there can be multiple extension instances, the IntelliJ Platform ensures that only one instance of a service/component is loaded,\nthus registering the same class as both an extension and a service/component may cause issues.\n\nNew in 2023.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "ExtensionRegisteredAsServiceOrComponent", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ForbiddenInSuspectContextMethod", + "shortDescription": { + "text": "Forbidden in suspend context method usage" + }, + "fullDescription": { + "text": "Reports inappropriate usages of methods in Kotlin coroutines, which uses threading context (annotated with '@RequiresBlockingContext'). Many of these methods have corresponding coroutine-friendly analogues, that can be used in 'suspend' contexts. Examples: 'com.intellij.openapi.progress.ProgressManager.checkCanceled()' should be replaced with 'com.intellij.openapi.progress.checkCancelled()' 'com.intellij.openapi.application.Application.invokeAndWait()' should be replaced with 'withContext(Dispatchers.EDT)' etc.", + "markdown": "Reports inappropriate usages of methods in Kotlin coroutines, which uses threading context (annotated with `@RequiresBlockingContext`). Many of these methods have corresponding coroutine-friendly analogues, that can be used in `suspend` contexts. Examples:\n\n* `com.intellij.openapi.progress.ProgressManager.checkCanceled()` should be replaced with `com.intellij.openapi.progress.checkCancelled()`\n* `com.intellij.openapi.application.Application.invokeAndWait()` should be replaced with `withContext(Dispatchers.EDT)`\n* etc." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ForbiddenInSuspectContextMethod", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NonDefaultConstructor", + "shortDescription": { + "text": "Non-default constructors for service and extension class" + }, + "fullDescription": { + "text": "Reports extension/service class having a non-default (empty) constructor. Other dependencies should be acquired when needed in corresponding methods only. Constructor having 'Project' for extension/service on the corresponding level is allowed.", + "markdown": "Reports extension/service class having a non-default (empty) constructor.\n\n\nOther dependencies should be acquired when needed in corresponding methods only.\nConstructor having `Project` for extension/service on the corresponding level is allowed." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "NonDefaultConstructor", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CancellationCheckInLoops", + "shortDescription": { + "text": "Cancellation check in loops" + }, + "fullDescription": { + "text": "Reports 'forEach' loops with missing cancellation checks. Runs only within the methods with 'com.intellij.util.concurrency.annotations.RequiresReadLock' annotation. Example: 'for (item in items) {\n ProgressManager.checkCanceled() // should be present in the first line\n ...\n }' In case of nested loops with nothing in between: 'for (item in items) {\n // nothing in between\n for (inner in item.inners) {\n ProgressManager.checkCanceled() // should be present in the first line of the inner loop only\n ...\n }\n }' In blocking context 'com.intellij.openapi.progress.ProgressManager#checkCanceled' should be used, while 'com.intellij.openapi.progress.CoroutinesKt#checkCancelled' should be used in suspending one. See Background Processes and ProcessCanceledException in IntelliJ Platform Plugin SDK docs for more details. New in 2023.1", + "markdown": "Reports `forEach` loops with missing cancellation checks.\n\nRuns only within the methods with `com.intellij.util.concurrency.annotations.RequiresReadLock` annotation.\n\n**Example:**\n\n\n for (item in items) {\n ProgressManager.checkCanceled() // should be present in the first line\n ...\n }\n\n\nIn case of nested loops with nothing in between:\n\n\n for (item in items) {\n // nothing in between\n for (inner in item.inners) {\n ProgressManager.checkCanceled() // should be present in the first line of the inner loop only\n ...\n }\n }\n\n\nIn blocking context `com.intellij.openapi.progress.ProgressManager#checkCanceled` should be used,\nwhile `com.intellij.openapi.progress.CoroutinesKt#checkCancelled` should be used in suspending one.\n\n\nSee [Background Processes and ProcessCanceledException](https://plugins.jetbrains.com/docs/intellij/general-threading-rules.html#background-processes-and-processcanceledexception) in IntelliJ Platform Plugin SDK docs for more details.\n\nNew in 2023.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CancellationCheckInLoops", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ApplicationServiceAsStaticFinalFieldOrProperty", + "shortDescription": { + "text": "Application service assigned to a static final field or immutable property" + }, + "fullDescription": { + "text": "Reports assignments of application services to static final fields / immutable properties. Static final fields (Java) or static immutable properties with backing fields (Kotlin) Note: Hereinafter, static in Kotlin refers to members of non-anonymous objects or top-level declarations. Such services' assignments contribute to global state and make it impossible to tear down an application and set up another one in tests, therefore, repeated tests in the same process may fail. The only exception is an explicit constructor call to store dummy/default instances. The recommended way to avoid storing services is to retrieve a service locally. Alternatively, one can wrap it in 'java.util.function.Supplier' (Java, Kotlin) or convert the property to a function (Kotlin). Example (Java): '// Bad:\nprivate static final ManagingFS ourInstance = ApplicationManager.getApplication().getService(ManagingFS.class);' '// Good:\nprivate static final Supplier ourInstance = CachedSingletonsRegistry.lazy(() -> {\n return ApplicationManager.getApplication().getService(ManagingFS.class);\n});' '// Exception:\nprivate static final UniqueVFilePathBuilder DUMMY_BUILDER = new UniqueVFilePathBuilder()' Retrieving a service instance through static immutable properties (Kotlin) While services' assignments to properties without backing fields don't cause the aforementioned problem, using an explicit 'getInstance()' method to retrieve a service is preferred over using a property: It makes it clearer on the call site that it can involve loading the service, which might not be cheap. Loading the service can throw an exception, and having an exception thrown by a method call is less surprising than if it was caused by property access. (Over-)using properties may be error-prone in a way that it might be accidentally changed to a property with an initializer instead of the correct (but more verbose) property with a getter, and that change can easily be overlooked. Using the method instead of a property keeps 'MyApplicationService.getInstance()' calls consistent when used both in Kotlin, and Java. Using the method keeps 'MyApplicationService.getInstance()' consistent with 'MyProjectService.getInstance(project)', both on the declaration and call sites. For better tooling performance, it is always advised to keep an explicit method return type. Example: '@Service\nclass MyApplicationService {\n companion object {\n @JvmStatic\n val instance: MyApplicationService // bad\n get() = service()\n }\n}' '@Service\nclass MyApplicationService {\n companion object {\n @JvmStatic\n fun getInstance(): MyApplicationService = service() // good\n }\n}' New in 2023.3", + "markdown": "Reports assignments of application services to static final fields / immutable properties.\n\n#### Static final fields (Java) or static immutable properties with backing fields (Kotlin)\n\n\n**Note:** Hereinafter, static in Kotlin refers to members of non-anonymous objects or top-level declarations.\n\n\nSuch services' assignments contribute to global state and make it impossible to tear down an application and set up another one in tests,\ntherefore, repeated tests in the same process may fail.\nThe only exception is an explicit constructor call to store dummy/default instances.\n\n\nThe recommended way to avoid storing services is to retrieve a service locally.\nAlternatively, one can wrap it in `java.util.function.Supplier` (Java, Kotlin)\nor convert the property to a function (Kotlin).\n\nExample (Java):\n\n\n // Bad:\n private static final ManagingFS ourInstance = ApplicationManager.getApplication().getService(ManagingFS.class);\n\n\n // Good:\n private static final Supplier ourInstance = CachedSingletonsRegistry.lazy(() -> {\n return ApplicationManager.getApplication().getService(ManagingFS.class);\n });\n\n\n // Exception:\n private static final UniqueVFilePathBuilder DUMMY_BUILDER = new UniqueVFilePathBuilder()\n\n#### Retrieving a service instance through static immutable properties (Kotlin)\n\n\nWhile services' assignments to properties without backing fields don't cause the aforementioned problem,\nusing an explicit `getInstance()` method to retrieve a service is preferred over using a property:\n\n* It makes it clearer on the call site that it can involve loading the service, which might not be cheap.\n* Loading the service can throw an exception, and having an exception thrown by a method call is less surprising than if it was caused by property access.\n* (Over-)using properties may be error-prone in a way that it might be accidentally changed to a property with an initializer instead of the correct (but more verbose) property with a getter, and that change can easily be overlooked.\n* Using the method instead of a property keeps `MyApplicationService.getInstance()` calls consistent when used both in Kotlin, and Java.\n* Using the method keeps `MyApplicationService.getInstance()` consistent with `MyProjectService.getInstance(project)`, both on the declaration and call sites.\n\nFor better tooling performance, it is always advised to keep an explicit method return type.\n\nExample:\n\n\n @Service\n class MyApplicationService {\n companion object {\n @JvmStatic\n val instance: MyApplicationService // bad\n get() = service()\n }\n }\n\n\n @Service\n class MyApplicationService {\n companion object {\n @JvmStatic\n fun getInstance(): MyApplicationService = service() // good\n }\n }\n\nNew in 2023.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ApplicationServiceAsStaticFinalFieldOrProperty", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PresentationAnnotation", + "shortDescription": { + "text": "Invalid icon path in @Presentation" + }, + "fullDescription": { + "text": "Reports invalid and deprecated value for 'icon' attribute in 'com.intellij.ide.presentation.Presentation' annotation.", + "markdown": "Reports invalid and deprecated value for `icon` attribute in `com.intellij.ide.presentation.Presentation` annotation." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "PresentationAnnotation", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UseVirtualFileEquals", + "shortDescription": { + "text": "Use 'VirtualFile#equals(Object)'" + }, + "fullDescription": { + "text": "Reports comparing 'VirtualFile' instances using '=='. Replace with 'equals()' call.", + "markdown": "Reports comparing `VirtualFile` instances using `==`.\n\n\nReplace with `equals()` call." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UseVirtualFileEquals", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StaticInitializationInExtensions", + "shortDescription": { + "text": "Static initialization in extension point implementations" + }, + "fullDescription": { + "text": "Reports static initialization in extension point implementations. Static initialization is performed once the class is loaded, which may cause excessive classloading or early initialization of heavy resources. Since extension point implementations are supposed to be cheap to create, they must not have static initializers. New in 2023.3", + "markdown": "Reports static initialization in [extension point implementations](https://plugins.jetbrains.com/docs/intellij/plugin-extensions.html).\n\n\nStatic initialization is performed once the class is loaded, which may cause excessive classloading or early initialization of heavy resources.\nSince extension point implementations are supposed to be cheap to create, they must not have static initializers.\n\nNew in 2023.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StaticInitializationInExtensions", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnspecifiedActionsPlace", + "shortDescription": { + "text": "Unspecified action place" + }, + "fullDescription": { + "text": "Reports passing unspecified 'place' parameter for 'ActionManager.createActionToolbar()' and 'ActionManager.createActionPopupMenu()'. Specifying proper 'place' is required to distinguish Action's usage in 'update()/actionPerformed()' via 'AnActionEvent.getPlace()'. Examples: '// bad:\nactionManager.createActionToolbar(\"\", group, false);\nactionManager.createActionToolbar(\"unknown\", group, false);\nactionManager.createActionPopupMenu(ActionPlaces.UNKNOWN, group);\n\n// good:\nactionManager.createActionToolbar(\"MyPlace\", group, false);\nactionManager.createActionPopupMenu(ActionPlaces.EDITOR_TOOLBAR, group);'", + "markdown": "Reports passing unspecified `place` parameter for `ActionManager.createActionToolbar()` and `ActionManager.createActionPopupMenu()`.\n\n\nSpecifying proper `place` is required to distinguish Action's usage in `update()/actionPerformed()` via `AnActionEvent.getPlace()`.\n\n\nExamples:\n\n\n // bad:\n actionManager.createActionToolbar(\"\", group, false);\n actionManager.createActionToolbar(\"unknown\", group, false);\n actionManager.createActionPopupMenu(ActionPlaces.UNKNOWN, group);\n\n // good:\n actionManager.createActionToolbar(\"MyPlace\", group, false);\n actionManager.createActionPopupMenu(ActionPlaces.EDITOR_TOOLBAR, group);\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnspecifiedActionsPlace", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MismatchedLightServiceLevelAndCtor", + "shortDescription": { + "text": "Mismatch between light service level and its constructor" + }, + "fullDescription": { + "text": "Reports mismatches between light service levels and its constructors. The following problems are reported: A light service class has a constructor with two parameters of types 'com.intellij.openapi.project.Project' and 'kotlinx.coroutines.CoroutineScope', or one parameter of type 'Project' is not annotated as a project-level service. A light service class annotated as an application-level service does not have a no-arg constructor, nor a constructor with one parameter of type 'CoroutineScope'. Example (Kotlin): '@Service // Suggest specifying 'Service.Level.PROJECT' parameter in '@Service' annotation\nclass MyService(val project: Project) // Suggest removing the parameter from constructor 'MyService'' After applying the quick-fix that suggests specifying 'Service.Level.PROJECT' parameter in '@Service' annotation is applied: '@Service(Service.Level.PROJECT)\nclass MyService(val project: Project)' After applying the quick-fix that suggests removing the parameter from constructor 'MyService': '@Service\nclass MyService()' New in 2023.2", + "markdown": "Reports mismatches between light service levels and its constructors.\n\nThe following problems are reported:\n\n* A light service class has a constructor with two parameters of types `com.intellij.openapi.project.Project` and `kotlinx.coroutines.CoroutineScope`, or one parameter of type `Project` is not annotated as a project-level service.\n* A light service class annotated as an application-level service does not have a no-arg constructor, nor a constructor with one parameter of type `CoroutineScope`.\n\nExample (Kotlin):\n\n\n @Service // Suggest specifying 'Service.Level.PROJECT' parameter in '@Service' annotation\n class MyService(val project: Project) // Suggest removing the parameter from constructor 'MyService'\n\nAfter applying the quick-fix that suggests specifying 'Service.Level.PROJECT' parameter in '@Service' annotation is applied:\n\n\n @Service(Service.Level.PROJECT)\n class MyService(val project: Project)\n\nAfter applying the quick-fix that suggests removing the parameter from constructor 'MyService':\n\n\n @Service\n class MyService()\n\nNew in 2023.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MismatchedLightServiceLevelAndCtor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ActionIsNotPreviewFriendly", + "shortDescription": { + "text": "Field blocks intention preview" + }, + "fullDescription": { + "text": "Reports fields in 'LocalQuickFix' and 'IntentionAction' implementations that prevent intention preview action from functioning properly. Additionally, excessive '@SafeFieldForPreview' annotations are reported on fields whose types are known to be safe. Intention preview is an IntelliJ platform feature that displays how quick-fix or intention action will change the current file when applied. To implement this in quick fixes, 'LocalQuickFix.generatePreview()' is called with a custom 'ProblemDescriptor' that points to the non-physical copy of current file. In intention actions, 'IntentionAction.generatePreview()' is called with the non-physical copy of current file and imaginary editor. Normally, these methods just delegate to 'LocalQuickFix.applyFix()' or 'IntentionAction.invoke()'. However, some quick-fixes may refer directly or indirectly to physical elements and use them for writing. As a result, preview won't work, as the quick-fix will attempt to update physical PSI instead of non-physical one. To avoid this, default implementation of 'generatePreview()' delegates only if all the instance fields of a quick fix or intention action class have safe types: primitives, Strings, etc. You may fix this problem in a number of ways: If the field does not actually store any PSI reference, or that PSI is used only for reading, you may annotate the field with '@SafeFieldForPreview'. You can also use '@SafeTypeForPreview' if the field type can never store any writable PSI reference. You may override 'getFileModifierForPreview()' method and create a copy of the quick-fix rebinding it to the non-physical file copy which is supplied as a parameter. Use 'PsiTreeUtil.findSameElementInCopy()' to find the corresponding PSI elements inside the supplied non-physical copy. Instead of storing PSI references in fields, try to extract all the necessary information from 'ProblemDescriptor.getPsiElement()' in quick fix or from the supplied file/editor in intention action. You may also inherit the abstract 'LocalQuickFixAndIntentionActionOnPsiElement' class and implement its 'invoke()' and 'isAvailable()' methods, which have 'startElement' and 'endElement' parameters. These parameters are automatically mapped to a non-physical file copy for you. You may override 'generatePreview()' method and provide completely custom preview behavior. For example, it's possible to display a custom HTML document instead of actual preview if your action does something besides modifying a current file. This inspection does not report if a custom implementation of 'getFileModifierForPreview()' or 'generatePreview()' exists. However, this doesn't mean that the implementation is correct and preview works. Please test. Also note that preview result is calculated in background thread, so you cannot start a write action during the preview or do any operation that requires a write action. Finally, no preview is generated automatically if 'startInWriteAction()' returns 'false'. In this case, having custom 'generatePreview()' implementation is desired. New in 2022.1", + "markdown": "Reports fields in `LocalQuickFix` and `IntentionAction` implementations that prevent intention preview action from functioning properly. Additionally, excessive `@SafeFieldForPreview` annotations are reported on fields whose types are known to be safe.\n\n\nIntention preview is an IntelliJ platform feature that displays how quick-fix or intention action\nwill change the current file when applied. To implement this in quick fixes,\n`LocalQuickFix.generatePreview()` is called with a custom `ProblemDescriptor`\nthat points to the non-physical copy of current file. In intention actions, `IntentionAction.generatePreview()`\nis called with the non-physical copy of current file and imaginary editor.\nNormally, these methods just delegate to `LocalQuickFix.applyFix()` or `IntentionAction.invoke()`.\nHowever, some quick-fixes may refer directly or indirectly to physical elements and use them for writing. As a result,\npreview won't work, as the quick-fix will attempt to update physical PSI instead of non-physical one.\nTo avoid this, default implementation of `generatePreview()` delegates only if all the\ninstance fields of a quick fix or intention action class have safe types: primitives, Strings, etc.\n\n\nYou may fix this problem in a number of ways:\n\n1. If the field does not actually store any PSI reference, or that PSI is used only for reading, you may annotate the field with `@SafeFieldForPreview`. You can also use `@SafeTypeForPreview` if the field type can never store any writable PSI reference.\n2. You may override `getFileModifierForPreview()` method and create a copy of the quick-fix rebinding it to the non-physical file copy which is supplied as a parameter. Use `PsiTreeUtil.findSameElementInCopy()` to find the corresponding PSI elements inside the supplied non-physical copy.\n3. Instead of storing PSI references in fields, try to extract all the necessary information from `ProblemDescriptor.getPsiElement()` in quick fix or from the supplied file/editor in intention action. You may also inherit the abstract `LocalQuickFixAndIntentionActionOnPsiElement` class and implement its `invoke()` and `isAvailable()` methods, which have `startElement` and `endElement` parameters. These parameters are automatically mapped to a non-physical file copy for you.\n4. You may override `generatePreview()` method and provide completely custom preview behavior. For example, it's possible to display a custom HTML document instead of actual preview if your action does something besides modifying a current file.\n\n\nThis inspection does not report if a custom implementation of `getFileModifierForPreview()`\nor `generatePreview()` exists. However, this doesn't mean that the implementation is correct and preview works.\nPlease test. Also note that preview result is calculated in background thread, so you cannot start a write action\nduring the preview or do any operation that requires a write action. Finally, no preview is generated automatically\nif `startInWriteAction()` returns `false`. In this case, having custom `generatePreview()`\nimplementation is desired.\n\nNew in 2022.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ActionIsNotPreviewFriendly", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LightServiceMigrationCode", + "shortDescription": { + "text": "A service can be converted to a light one" + }, + "fullDescription": { + "text": "Reports classes that can be marked as light services using the '@com.intellij.openapi.components.Service' annotation instead of being registered as services in 'plugin.xml' A service that is not intended for overriding is not required to be registered in the 'plugin.xml' file. Instead, annotate the service class with the '@Service' annotation. For project-level services, specify '@Service(Service.Level.PROJECT)'. Requirements: IntelliJ Platform 2019.3 or newer. Service class must be 'final'. 'serviceInterface' is not specified. If the application-level service is a 'com.intellij.openapi.components.PersistentStateComponent', roaming must be disabled ('roamingType = RoamingType.DISABLED'). None of these attributes is specified: 'os', 'client', 'overrides', 'id', 'preload'. See Services in IntelliJ Platform Plugin SDK docs for more details. See also the 'Plugin DevKit | Plugin descriptor | A service can be converted to a light one' inspection. New in 2023.2", + "markdown": "Reports classes that can be marked as light services using the `@com.intellij.openapi.components.Service` annotation instead of being registered as services in `plugin.xml`\n\n\nA service that is not intended for overriding is not required to be registered in the `plugin.xml` file.\nInstead, annotate the service class with the `@Service` annotation. For project-level services, specify\n`@Service(Service.Level.PROJECT)`.\n\n\nRequirements:\n\n* IntelliJ Platform 2019.3 or newer.\n* Service class must be `final`.\n* `serviceInterface` is not specified.\n* If the application-level service is a `com.intellij.openapi.components.PersistentStateComponent`, roaming must be disabled (`roamingType = RoamingType.DISABLED`).\n* None of these attributes is specified: `os`, `client`, `overrides`, `id`, `preload`.\n\n\nSee [Services](https://plugins.jetbrains.com/docs/intellij/plugin-services.html) in IntelliJ Platform Plugin SDK docs for more details.\n\n\nSee also the `Plugin DevKit | Plugin descriptor | A service can be converted to a light one` inspection.\n\nNew in 2023.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LightServiceMigrationCode", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InspectionUsingGrayColors", + "shortDescription": { + "text": "Using new Color(a,a,a)" + }, + "fullDescription": { + "text": "Reports usages of 'java.awt.Color' to create gray colors. The Convert to 'Gray' quick fix replaces it using 'com.intellij.ui.Gray' constants instead. Examples: '// bad:\nColor gray = new Color(37, 37, 37);\n\n// good:\nColor gray = Gray._37;'", + "markdown": "Reports usages of `java.awt.Color` to create gray colors.\n\n\nThe **Convert to 'Gray'** quick fix replaces it using `com.intellij.ui.Gray` constants instead.\n\nExamples:\n\n\n // bad:\n Color gray = new Color(37, 37, 37);\n\n // good:\n Color gray = Gray._37;\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "InspectionUsingGrayColors", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IntentionDescriptionNotFoundInspection", + "shortDescription": { + "text": "Intention description checker" + }, + "fullDescription": { + "text": "Reports intentions that are missing an HTML description file, 'before.template' file or 'after.template' file. These are shown in Settings | Editor | Intentions. The Create description file quick-fix creates a template HTML description file.", + "markdown": "Reports intentions that are missing an HTML description file, `before.template` file or `after.template` file. These are shown in [Settings \\| Editor \\| Intentions](settings://preferences.intentionPowerPack).\n\n\nThe **Create description file** quick-fix creates a template HTML description file." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "IntentionDescriptionNotFoundInspection", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Description file", + "index": 104, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IncorrectServiceRetrieving", + "shortDescription": { + "text": "Incorrect service retrieving" + }, + "fullDescription": { + "text": "Reports the following problems when retrieving services: Attempts to retrieve an unregistered service. Mismatch when retrieving a service: attempting to get a project-level service as an application-level service, or vice versa. Example (Kotlin): '@Service\nclass MyAppService\n\n@Service(Service.Level.PROJECT)\nclass MyProjectService(private val project: Project)' '// Bad:\nval projectService = service() // The project-level service is retrieved as an application-level service\nval applicationService = project.service() // The application-level service is retrieved as a project-level service' '// Good:\nval projectService = project.service()\nval applicationService = service();' New in 2023.2", + "markdown": "Reports the following problems when retrieving services:\n\n* Attempts to retrieve an unregistered service.\n* Mismatch when retrieving a service: attempting to get a project-level service as an application-level service, or vice versa.\n\nExample (Kotlin):\n\n\n @Service\n class MyAppService\n\n @Service(Service.Level.PROJECT)\n class MyProjectService(private val project: Project)\n\n\n // Bad:\n val projectService = service() // The project-level service is retrieved as an application-level service\n val applicationService = project.service() // The application-level service is retrieved as a project-level service\n\n\n // Good:\n val projectService = project.service()\n val applicationService = service();\n\nNew in 2023.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "IncorrectServiceRetrieving", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PluginXmlDynamicPlugin", + "shortDescription": { + "text": "Plugin.xml dynamic plugin verification" + }, + "fullDescription": { + "text": "Reports dynamic plugin problems. Dynamic plugins can be installed, updated and uninstalled without restarting the IDE (supported in 2020.1 and later). Please see Dynamic Plugins for further reference. New in 2020.1", + "markdown": "Reports dynamic plugin problems.\n\n\nDynamic plugins can be installed, updated and uninstalled without restarting the IDE (supported in 2020.1 and later).\n\n\nPlease see [Dynamic Plugins](https://plugins.jetbrains.com/docs/intellij/dynamic-plugins.html?from=PluginXmlDynamicPlugin) for further reference.\n\nNew in 2020.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PluginXmlDynamicPlugin", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Plugin descriptor", + "index": 76, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ComponentRegistrationProblems", + "shortDescription": { + "text": "Component type mismatch" + }, + "fullDescription": { + "text": "Reports incorrect registration of plugin components (Actions and Components). The following problems are reported: Action/Component implementation class is abstract. Class is registered in plugin.xml as action but does not extend 'AnAction' class. Action class does not have a public no-argument constructor.", + "markdown": "Reports incorrect registration of plugin components (Actions and Components).\n\n\nThe following problems are reported:\n\n* Action/Component implementation class is abstract.\n* Class is registered in plugin.xml as action but does not extend `AnAction` class.\n* Action class does not have a public no-argument constructor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "ComponentRegistrationProblems", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LightServiceMustBeFinal", + "shortDescription": { + "text": "Light service must be final" + }, + "fullDescription": { + "text": "Reports classes annotated with the '@com.intellij.openapi.components.Service' annotation that are not final. Suggests making a class final if it is concrete. Example: '// MyService.kt\n @Service(Service.Level.APP)\n open class MyService' After the quick-fix is applied: '// MyService.kt\n @Service(Service.Level.APP)\n class MyService' Suggests removing the '@Service' annotation if it is an abstract class or interface. Example: '// MyService.java\n @Service(Service.Level.APP)\n abstract class MyService {}' After the quick-fix is applied: '// MyService.java\n abstract class MyService {}' New in 2023.2", + "markdown": "Reports classes annotated with the `@com.intellij.openapi.components.Service` annotation that are not final.\n\n\nSuggests making a class final if it is concrete.\n\n**Example:**\n\n\n // MyService.kt\n @Service(Service.Level.APP)\n open class MyService\n\nAfter the quick-fix is applied:\n\n\n // MyService.kt\n @Service(Service.Level.APP)\n class MyService\n\n\nSuggests removing the `@Service` annotation if it is an abstract class or interface.\n\n**Example:**\n\n\n // MyService.java\n @Service(Service.Level.APP)\n abstract class MyService {}\n\nAfter the quick-fix is applied:\n\n\n // MyService.java\n abstract class MyService {}\n\nNew in 2023.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "LightServiceMustBeFinal", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MissingRecentApi", + "shortDescription": { + "text": "Usage of IntelliJ API not available in older IDEs" + }, + "fullDescription": { + "text": "Reports usages of IntelliJ Platform API introduced in a version newer than the one specified in '' '@since-build' in 'plugin.xml'. Using such API may lead to incompatibilities of the plugin with older IDE versions. To avoid possible issues when running the plugin in older IDE versions, increase 'since-build' accordingly, or remove usages of this API. See Build Number Ranges in IntelliJ Platform Plugin SDK docs for more details. Configure the inspection: If '' '@since/until-build' attributes are not specified in 'plugin.xml', set Since/Until explicitly.", + "markdown": "Reports usages of IntelliJ Platform API introduced in a version *newer* than the one specified in `` `@since-build` in `plugin.xml`.\n\n\nUsing such API may lead to incompatibilities of the plugin with older IDE versions.\n\n\nTo avoid possible issues when running the plugin in older IDE versions, increase `since-build` accordingly,\nor remove usages of this API.\n\n\nSee [Build Number Ranges](https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html) in IntelliJ Platform Plugin SDK docs for more details.\n\nConfigure the inspection:\nIf `` `@since/until-build` attributes are not specified in `plugin.xml`, set **Since** /**Until** explicitly." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "MissingRecentApi", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LightServiceMigrationXML", + "shortDescription": { + "text": "A service can be converted to a light one" + }, + "fullDescription": { + "text": "Reports services registered in 'plugin.xml' that can be converted to light ones. A service that is not intended for overriding is not required to be registered in the 'plugin.xml' file. Instead, annotate the service class with the '@com.intellij.openapi.components.Service' annotation. For project-level services, specify '@Service(Service.Level.PROJECT)'. Requirements: IntelliJ Platform 2019.3 or newer. Service class must be 'final'. 'serviceInterface' is not specified. If the application-level service is a 'com.intellij.openapi.components.PersistentStateComponent', roaming must be disabled ('roamingType = RoamingType.DISABLED'). None of these attributes is specified: 'os', 'client', 'overrides', 'id', 'preload'. Also reports services registered in 'plugin.xml' whose classes are already annotated with '@Service'. See Services in IntelliJ Platform Plugin SDK docs for more details. See also the 'Plugin DevKit | Code | A service can be converted to a light one' inspection. New in 2023.2", + "markdown": "Reports services registered in `plugin.xml` that can be converted to light ones.\n\n\nA service that is not intended for overriding is not required to be registered in the `plugin.xml` file.\nInstead, annotate the service class with the `@com.intellij.openapi.components.Service` annotation. For\nproject-level services, specify `@Service(Service.Level.PROJECT)`.\n\n\nRequirements:\n\n* IntelliJ Platform 2019.3 or newer.\n* Service class must be `final`.\n* `serviceInterface` is not specified.\n* If the application-level service is a `com.intellij.openapi.components.PersistentStateComponent`, roaming must be disabled (`roamingType = RoamingType.DISABLED`).\n* None of these attributes is specified: `os`, `client`, `overrides`, `id`, `preload`.\n\nAlso reports services registered in `plugin.xml` whose classes are already annotated with `@Service`.\n\n\nSee [Services](https://plugins.jetbrains.com/docs/intellij/plugin-services.html) in IntelliJ Platform Plugin SDK docs for more details.\n\n\nSee also the `Plugin DevKit | Code | A service can be converted to a light one` inspection.\n\nNew in 2023.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LightServiceMigrationXML", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Plugin descriptor", + "index": 76, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KotlinObjectExtensionRegistration", + "shortDescription": { + "text": "Extension class is a Kotlin object" + }, + "fullDescription": { + "text": "Reports extensions which are instantiated by the IntelliJ Platform, but are declared as Kotlin objects. Extensions lifecycle is managed by the IntelliJ Platform. Using Kotlin objects for extension registration may cause creation of unnecessary extension instances and make plugin unloading impossible. Example Extension registration: '' Extension implementation: '// bad:\nobject MyAnnotator : Annotator {\n ...\n}\n\n// good:\nclass MyAnnotator : Annotator {\n ...\n}' New in 2023.1", + "markdown": "Reports extensions which are instantiated by the IntelliJ Platform, but are declared as Kotlin objects.\n\n\nExtensions lifecycle is managed by the IntelliJ Platform.\nUsing Kotlin objects for extension registration may cause creation of unnecessary extension instances and make plugin unloading\nimpossible.\n\nExample\n-------\n\nExtension registration:\n\n\n \n\nExtension implementation:\n\n\n // bad:\n object MyAnnotator : Annotator {\n ...\n }\n\n // good:\n class MyAnnotator : Annotator {\n ...\n }\n\nNew in 2023.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "KotlinObjectExtensionRegistration", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Plugin descriptor", + "index": 76, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MissingAccessibleContext", + "shortDescription": { + "text": "Accessible context is missing" + }, + "fullDescription": { + "text": "Reports Swing components that do not provide accessibility context. This information is used by screen readers. Failing to provide it makes the component inaccessible for visually impaired users. Example: 'ListCellRenderer renderer = (list, val, index, sel, cell) -> {\n JPanel panel = new JPanel();\n return panel;\n };' To fix the problem, you should either call 'setAccessibleName()' on the returned 'JPanel' or override its 'getAccessibleContext()' method. The returned text should reflect the purpose of the component. For example, in the case of 'ListCellRenderer', this would be the text of the menu item.", + "markdown": "Reports Swing components that do not provide accessibility context.\n\n\nThis information is used by screen readers. Failing to provide it makes the component inaccessible for\nvisually impaired users.\n\n**Example:**\n\n\n ListCellRenderer renderer = (list, val, index, sel, cell) -> {\n JPanel panel = new JPanel();\n return panel;\n };\n\n\nTo fix the problem, you should either call `setAccessibleName()` on the returned `JPanel`\nor override its `getAccessibleContext()` method.\n\n\nThe returned text should reflect the purpose\nof the component. For example, in the case of `ListCellRenderer`, this would be the text of the menu\nitem." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MissingAccessibleContext", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SimplifiableServiceRetrieving", + "shortDescription": { + "text": "Simplifiable service retrieving" + }, + "fullDescription": { + "text": "Reports service getting calls that can be replaced with a calls to an existing static 'getInstance()' or 'getInstance(Project)' methods. Example (Java): '@Service\npublic class MyAppService {\n public static MyAppService getInstance() {\n return ApplicationManager.getApplication().getService(MyAppService.class);\n }\n}\n\n@Service(Service.Level.PROJECT)\npublic class MyProjectService {\n public static MyProjectService getInstance(Project project) {\n return project.getService(MyProjectService.class);\n }\n}' '// Bad:\nMyAppService applicationService = ApplicationManager.getApplication().getService(MyAppService.class);\nMyProjectService projectService = project.getService(MyProjectService.class);' '// Good:\nMyAppService applicationService = MyAppService.getInstance();\nMyProjectService projectService = MyProjectService.getInstance(project);' New in 2023.2", + "markdown": "Reports service getting calls that can be replaced with a calls to an existing static `getInstance()` or `getInstance(Project)` methods.\n\nExample (Java):\n\n\n @Service\n public class MyAppService {\n public static MyAppService getInstance() {\n return ApplicationManager.getApplication().getService(MyAppService.class);\n }\n }\n\n @Service(Service.Level.PROJECT)\n public class MyProjectService {\n public static MyProjectService getInstance(Project project) {\n return project.getService(MyProjectService.class);\n }\n }\n\n\n // Bad:\n MyAppService applicationService = ApplicationManager.getApplication().getService(MyAppService.class);\n MyProjectService projectService = project.getService(MyProjectService.class);\n\n\n // Good:\n MyAppService applicationService = MyAppService.getInstance();\n MyProjectService projectService = MyProjectService.getInstance(project);\n\nNew in 2023.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "SimplifiableServiceRetrieving", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UseJBColor", + "shortDescription": { + "text": "Use Darcula aware JBColor" + }, + "fullDescription": { + "text": "Reports usages of 'java.awt.Color'. These are not aware of \"dark\" themes (e.g., bundled \"Darcula\") and might result in bad looking UI. Quick-fix replaces usages with 'JBColor', which defines \"dark\" color variant. Examples: '// bad:\nColor darkGreen = new Color(12, 58, 27);\nColor blue = Color.BLUE;\n\n// good:\nColor darkGreen = new JBColor(12, 58, 27);\nColor blue = JBColor.BLUE;\nColor green = new JBColor(new Color(12, 58, 27), new Color(27, 112, 39));'", + "markdown": "Reports usages of `java.awt.Color`.\n\n\nThese are not aware of \"dark\" themes (e.g., bundled \"Darcula\") and might result in bad looking UI.\n\n\nQuick-fix replaces usages with `JBColor`, which defines \"dark\" color variant.\n\nExamples:\n\n\n // bad:\n Color darkGreen = new Color(12, 58, 27);\n Color blue = Color.BLUE;\n\n // good:\n Color darkGreen = new JBColor(12, 58, 27);\n Color blue = JBColor.BLUE;\n Color green = new JBColor(new Color(12, 58, 27), new Color(27, 112, 39));\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UseJBColor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PluginXmlExtensionRegistration", + "shortDescription": { + "text": "Plugin.xml extension registration" + }, + "fullDescription": { + "text": "Reports problems with extension registration in 'plugin.xml'. The following problems are reported: Missing 'language' declaration. If the extension does not target a specific language, use quick fix to create an explicit declaration for \"any language\". Inspections: missing attributes Services: redundant 'serviceInterface' declaration 'com.intellij.stubElementTypeHolder' without 'externalIdPrefix', see Stub Indexes 'com.intellij.statusBarWidgetFactory' without 'id' New in 2022.3", + "markdown": "Reports problems with extension registration in `plugin.xml`.\n\n\nThe following problems are reported:\n\n* Missing `language` declaration. If the extension does not target a specific language, use quick fix to create an explicit declaration for \"any language\".\n* Inspections: missing attributes\n* Services: redundant `serviceInterface` declaration\n* `com.intellij.stubElementTypeHolder` without `externalIdPrefix`, see [Stub Indexes](https://plugins.jetbrains.com/docs/intellij/stub-indexes.html?from=?from=DevkitPluginXmlInspectionDescription)\n* `com.intellij.statusBarWidgetFactory` without `id`\n\nNew in 2022.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PluginXmlExtensionRegistration", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Plugin descriptor", + "index": 76, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UElementAsPsi", + "shortDescription": { + "text": "UElement as PsiElement usage" + }, + "fullDescription": { + "text": "Reports usage of UAST 'UElement' as 'PsiElement'. The 'PsiElement' obtained this way is ambiguous. To obtain \"physical\" 'PsiElement' use 'UElementKt.getSourcePsiElement()', for 'PsiElement' that \"emulates\" behaviour of Java-elements ('PsiClass', 'PsiMethod', etc.) use 'UElementKt.getAsJavaPsiElement()'. See UAST - Unified Abstract Syntax Tree in SDK Docs.", + "markdown": "Reports usage of UAST `UElement` as `PsiElement`.\n\n\nThe `PsiElement` obtained this way is ambiguous.\n\n\nTo obtain \"physical\" `PsiElement` use `UElementKt.getSourcePsiElement()`,\nfor `PsiElement` that \"emulates\" behaviour of Java-elements (`PsiClass`, `PsiMethod`, etc.)\nuse `UElementKt.getAsJavaPsiElement()`.\n\n\nSee [UAST - Unified Abstract Syntax Tree](https://plugins.jetbrains.com/docs/intellij/uast.html) in SDK Docs." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UElementAsPsi", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MissingActionUpdateThread", + "shortDescription": { + "text": "ActionUpdateThread is missing" + }, + "fullDescription": { + "text": "Reports actions, action groups and other 'ActionUpdateThreadAware' classes that implicitly state the deprecated and costly 'ActionUpdateThread.OLD_EDT' mode. When an action or an action group defines its own 'update()' method, IntelliJ Platform tries to mimic the old synchronous way of calling 'update()' and 'getChildren()' methods in the UI thread and supply it with all the data in 'AnActionEvent.dataContext()'. To do that, it caches all the possible data on a background thread beforehand, even if it is not needed. Provide one of the two new modes 'ActionUpdateThread.EDT' or 'ActionUpdateThread.BGT' by overriding the 'getActionUpdateThread()' method. See the documentation of 'ActionUpdateThread' for more information.", + "markdown": "Reports actions, action groups and other `ActionUpdateThreadAware` classes that implicitly state the deprecated and costly `ActionUpdateThread.OLD_EDT` mode.\n\n\nWhen an action or an action group defines its own `update()` method, IntelliJ Platform tries to mimic\nthe old synchronous way of calling `update()` and `getChildren()` methods in the UI thread and\nsupply it with all the data in `AnActionEvent.dataContext()`.\nTo do that, it caches all the possible data on a background thread beforehand, even if it is not needed.\n\n\nProvide one of the two new modes `ActionUpdateThread.EDT` or `ActionUpdateThread.BGT`\nby overriding the `getActionUpdateThread()` method.\n\n\nSee the documentation of `ActionUpdateThread` for more information." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MissingActionUpdateThread", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PluginXmlI18n", + "shortDescription": { + "text": "Plugin.xml i18n verification" + }, + "fullDescription": { + "text": "Reports hardcoded texts in 'plugin.xml'. Using texts defined in resource bundles allows supporting multiple languages in the IDE. The following elements are checked: ', ' '' known extension points having 'bundle/key' alternative", + "markdown": "Reports hardcoded texts in `plugin.xml`.\n\n\nUsing texts defined in resource bundles allows supporting multiple languages in the IDE.\n\n\nThe following elements are checked:\n\n* `, `\n* ``\n* known extension points having `bundle/key` alternative" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PluginXmlI18n", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Plugin descriptor", + "index": 76, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UsePrimitiveTypes", + "shortDescription": { + "text": "Use 'PsiType#equals(Object)' with primitive types" + }, + "fullDescription": { + "text": "Reports comparing 'PsiPrimitiveType' instances using '=='. Primitive types should be compared with 'equals' as Java 8 type annotations are also applicable for them. Replace with 'equals()' call.", + "markdown": "Reports comparing `PsiPrimitiveType` instances using `==`.\n\n\nPrimitive types should be compared with `equals` as Java 8 type annotations are also applicable for them.\n\n\nReplace with `equals()` call." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UsePrimitiveTypes", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "QuickFixGetFamilyNameViolation", + "shortDescription": { + "text": "QuickFix's getFamilyName() implementation must not depend on a specific context" + }, + "fullDescription": { + "text": "Reports 'QuickFix#getFamilyName()' using contextual information. This method must not use any non-static information.", + "markdown": "Reports `QuickFix#getFamilyName()` using contextual information.\n\n\nThis method must not use any non-static information." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "QuickFixGetFamilyNameViolation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UsePluginIdEquals", + "shortDescription": { + "text": "Use 'PluginId#equals(Object)'" + }, + "fullDescription": { + "text": "Reports comparing 'PluginId' instances using '=='. Replace with 'equals()' call.", + "markdown": "Reports comparing `PluginId` instances using `==`.\n\n\nReplace with `equals()` call." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UsePluginIdEquals", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "StatefulEp", + "shortDescription": { + "text": "Stateful extension" + }, + "fullDescription": { + "text": "Reports extensions and quick-fixes holding potentially leaking state. Keeping references to 'PsiElement', 'PsiReference', or 'Project' instances can result in memory leaks. Ideally, these should be stateless. For quick-fix, see 'LocalQuickFixOnPsiElement' as a convenient base class.", + "markdown": "Reports extensions and quick-fixes holding potentially leaking state.\n\n\nKeeping references to `PsiElement`, `PsiReference`, or `Project` instances can result in memory leaks.\n\n\nIdeally, these should be stateless.\nFor quick-fix, see `LocalQuickFixOnPsiElement` as a convenient base class." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "StatefulEp", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IncorrectParentDisposable", + "shortDescription": { + "text": "Incorrect parentDisposable parameter" + }, + "fullDescription": { + "text": "Reports using 'Application' or 'Project' as a parent 'Disposable' in plugin code. Such usages will lead to plugins not being unloaded correctly. Please see Choosing a Disposable Parent in SDK Docs.", + "markdown": "Reports using `Application` or `Project` as a parent `Disposable` in plugin code.\n\n\nSuch usages will lead to plugins not being unloaded correctly.\nPlease see [Choosing a\nDisposable Parent](https://plugins.jetbrains.com/docs/intellij/disposers.html?from=IncorrectParentDisposable#choosing-a-disposable-parent) in SDK Docs." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "IncorrectParentDisposable", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ActionPresentationInstantiatedInCtor", + "shortDescription": { + "text": "Eager creation of action presentation" + }, + "fullDescription": { + "text": "Reports any actions that are registered in the 'plugin.xml' file and instantiate the 'com.intellij.openapi.actionSystem.Presentation' object in their constructors. Any of the constructors of 'AnAction' with parameters instantiate the 'Presentation' object. However, instantiating the 'Presentation' object in constructor results in allocating resources, which may not be necessary. Instead of creating an instance of 'Presentation' that stores text, description, or icon, it is more efficient to utilize no-argument constructors of 'AnAction' and other base classes and follow the convention for setting the text, description, and icon in 'plugin.xml'. The IDE will load text, description, and icon only when the action is actually displayed in the UI. The convention for setting the text, description, and icon is as follows: Set the 'id' attribute for the action in the 'plugin.xml' file. Optionally, set the 'icon' attribute if an icon is needed. Set the text and description in the associated message bundle (it could be overridden in ''): 'action..text=Translated Action Text' 'action..description=Translated Action Description' Bad example: '// NewKotlinFileAction.kt\n internal class NewKotlinFileAction : AnAction(\n KotlinBundle.message(\"action.new.file.text\"),\n KotlinBundle.message(\"action.new.file.description\"),\n KotlinIcons.FILE\n )' '' Good example: '// NewKotlinFileAction.kt\n internal class NewKotlinFileAction : AnAction()' '\n ' '# KotlinBundle.properties\n action.Kotlin.NewFile.text=Kotlin Class/File\n action.Kotlin.NewFile.description=Creates a new Kotlin class or file' New in 2023.2", + "markdown": "Reports any actions that are registered in the `plugin.xml` file and instantiate\nthe `com.intellij.openapi.actionSystem.Presentation` object in their constructors.\n\n\nAny of the constructors of `AnAction` with parameters instantiate the `Presentation`\nobject. However, instantiating the `Presentation` object in constructor results in allocating\nresources, which may not be necessary. Instead of creating an instance of `Presentation` that\nstores text, description, or icon, it is more efficient to utilize no-argument constructors of\n`AnAction` and other base classes and follow the convention for setting the text, description,\nand icon in `plugin.xml`. The IDE will load text, description, and icon only when the action\nis actually displayed in the UI.\n\n\nThe convention for setting the text, description, and icon is as follows:\n\n* Set the `id` attribute for the action in the `plugin.xml` file.\n* Optionally, set the `icon` attribute if an icon is needed.\n* Set the text and description in the associated message bundle (it could be overridden in ``):\n * `action..text=Translated Action Text`\n * `action..description=Translated Action Description`\n\n**Bad example:**\n\n\n // NewKotlinFileAction.kt\n internal class NewKotlinFileAction : AnAction(\n KotlinBundle.message(\"action.new.file.text\"),\n KotlinBundle.message(\"action.new.file.description\"),\n KotlinIcons.FILE\n )\n\n\n \n \n\n**Good example:**\n\n\n // NewKotlinFileAction.kt\n internal class NewKotlinFileAction : AnAction()\n\n\n \n \n \n\n\n # KotlinBundle.properties\n action.Kotlin.NewFile.text=Kotlin Class/File\n action.Kotlin.NewFile.description=Creates a new Kotlin class or file\n\nNew in 2023.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ActionPresentationInstantiatedInCtor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TokenSetInParserDefinition", + "shortDescription": { + "text": "Non-platform TokenSet declared in ParserDefinition" + }, + "fullDescription": { + "text": "Reports 'TokenSet' field declarations referencing non-platform element types in 'ParserDefinition' classes. All languages 'ParserDefinition' are created on the application startup. Declaring a 'TokenSet' referencing non-platform language element types may cause creating and registering all the language element types in the holder class of the referenced type, even if a project doesn't contain any files in this language. Example: '// element types holder:\npublic interface MyLangTokenTypes {\n IElementType COMMENT = new MyLangTokenType(\"COMMENT\");\n IElementType TYPE1 = new MyLangTokenType(\"TYPE1\");\n IElementType TYPE2 = new MyLangTokenType(\"TYPE2\");\n // more types...\n}\n\n\n// bad:\n\npublic class MyLangParserDefinition implements ParserDefinition {\n // this field causes initializing and registering all the types from MyLangTokenTypes:\n private static final TokenSet COMMENTS = TokenSet.create(MyLangTokenTypes.COMMENT);\n\n @NotNull\n @Override\n public TokenSet getCommentTokens() {\n return COMMENTS;\n }\n ...\n}\n\n\n// good:\n\npublic final class MyLangTokenSets {\n public static final TokenSet COMMENTS = TokenSet.create(MyLangTokenTypes.COMMENT);\n}\n\npublic class MyLangParserDefinition implements ParserDefinition {\n @NotNull\n @Override\n public TokenSet getCommentTokens() {\n // types are referenced and registered only when this method is called:\n return MyLangTokenSets.COMMENTS;\n }\n ...\n}\n\n// good (Kotlin):\n\n// top-level declaration is not loaded until getCommentTokens() method is called:\nprivate val COMMENTS = TokenSet.create(MyLangTokenTypes.COMMENT);\n\nclass MyLangParserDefinition : ParserDefinition {\n override getCommentTokens(): TokenSet {\n return COMMENTS;\n }\n ...\n}\n\n// good:\n\npublic class MyLangParserDefinition implements ParserDefinition {\n // allowed platform TokenSet:\n private static final TokenSet COMMENTS1 = TokenSet.EMPTY;\n // allowed platform TokenType:\n private static final TokenSet COMMENTS2 = TokenSet.create(TokenType.WHITE_SPACE);\n\n @NotNull\n @Override\n public TokenSet getCommentTokens() {\n ...\n }\n ...\n}' New in 2023.2", + "markdown": "Reports `TokenSet` field declarations referencing non-platform element types in `ParserDefinition` classes.\n\n\nAll languages `ParserDefinition` are created on the application startup.\nDeclaring a `TokenSet` referencing non-platform language element types may cause creating and registering\nall the language element types in the holder class of the referenced type, even if a project doesn't contain any files in this language.\n\nExample:\n\n\n // element types holder:\n public interface MyLangTokenTypes {\n IElementType COMMENT = new MyLangTokenType(\"COMMENT\");\n IElementType TYPE1 = new MyLangTokenType(\"TYPE1\");\n IElementType TYPE2 = new MyLangTokenType(\"TYPE2\");\n // more types...\n }\n\n\n // bad:\n\n public class MyLangParserDefinition implements ParserDefinition {\n // this field causes initializing and registering all the types from MyLangTokenTypes:\n private static final TokenSet COMMENTS = TokenSet.create(MyLangTokenTypes.COMMENT);\n\n @NotNull\n @Override\n public TokenSet getCommentTokens() {\n return COMMENTS;\n }\n ...\n }\n\n\n // good:\n\n public final class MyLangTokenSets {\n public static final TokenSet COMMENTS = TokenSet.create(MyLangTokenTypes.COMMENT);\n }\n\n public class MyLangParserDefinition implements ParserDefinition {\n @NotNull\n @Override\n public TokenSet getCommentTokens() {\n // types are referenced and registered only when this method is called:\n return MyLangTokenSets.COMMENTS;\n }\n ...\n }\n\n // good (Kotlin):\n\n // top-level declaration is not loaded until getCommentTokens() method is called:\n private val COMMENTS = TokenSet.create(MyLangTokenTypes.COMMENT);\n\n class MyLangParserDefinition : ParserDefinition {\n override getCommentTokens(): TokenSet {\n return COMMENTS;\n }\n ...\n }\n\n // good:\n\n public class MyLangParserDefinition implements ParserDefinition {\n // allowed platform TokenSet:\n private static final TokenSet COMMENTS1 = TokenSet.EMPTY;\n // allowed platform TokenType:\n private static final TokenSet COMMENTS2 = TokenSet.create(TokenType.WHITE_SPACE);\n\n @NotNull\n @Override\n public TokenSet getCommentTokens() {\n ...\n }\n ...\n }\n\nNew in 2023.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "TokenSetInParserDefinition", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "IncorrectProcessCanceledExceptionHandling", + "shortDescription": { + "text": "'ProcessCanceledException' handled incorrectly" + }, + "fullDescription": { + "text": "Reports 'ProcessCanceledException's handled in an incorrect way. 'ProcessCanceledException' and its inheritors must not be caught, swallowed, logged, or handled in any way. Instead, it must be rethrown so that the infrastructure can handle it correctly. Inspection reports both explicit 'ProcessCanceledException' or its inheritors catching, as well as catching 'RuntimeException', 'Exception' and 'Throwable' covering 'ProcessCanceledException'. Example: '// bad:\ntry {\n // ...\n} catch (ProcessCanceledException e) { // exception should not be swallowed\n}\n\n// bad:\ntry {\n // ...\n} catch (ProcessCanceledException e) {\n LOG.error(\"Error occurred\", e); // exception should not be logged\n throw e;\n}\n\n\n// good:\ntry {\n // ...\n} catch (ProcessCanceledException e) {\n // additional actions\n throw e;\n}' New in 2023.2", + "markdown": "Reports `ProcessCanceledException`s handled in an incorrect way.\n\n\n`ProcessCanceledException` and its inheritors must not be caught, swallowed, logged, or handled in any way.\nInstead, it must be rethrown so that the infrastructure can handle it correctly.\n\n\nInspection reports both explicit `ProcessCanceledException` or its inheritors catching,\nas well as catching `RuntimeException`, `Exception` and `Throwable` covering `ProcessCanceledException`.\n\nExample:\n\n\n // bad:\n try {\n // ...\n } catch (ProcessCanceledException e) { // exception should not be swallowed\n }\n\n // bad:\n try {\n // ...\n } catch (ProcessCanceledException e) {\n LOG.error(\"Error occurred\", e); // exception should not be logged\n throw e;\n }\n\n\n // good:\n try {\n // ...\n } catch (ProcessCanceledException e) {\n // additional actions\n throw e;\n }\n\nNew in 2023.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "IncorrectProcessCanceledExceptionHandling", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CompanionObjectInExtension", + "shortDescription": { + "text": "Companion object in extensions" + }, + "fullDescription": { + "text": "Reports incorrect companion objects' usage in extensions. Kotlin companion object is always created once you try to load its containing class, and extension point implementations are supposed to be cheap to create. Excessive classloading in plugins is a huge problem for IDE startup. Bad pattern: 'class KotlinDocumentationProvider : AbstractDocumentationProvider(), ExternalDocumentationProvider {\n\n companion object {\n private val LOG = Logger.getInstance(KotlinDocumentationProvider::class.java)\n private val javaDocumentProvider = JavaDocumentationProvider()\n }\n }' Here 'KotlinDocumentationProvider' is an extension registered in 'plugin.xml': '' In this example 'JavaDocumentationProvider' will be loaded from disk once someone just calls 'new KotlinDocumentationProvider()'. Kotlin companion objects in extension point implementation can only contain a logger and simple constants. Other declarations may cause excessive classloading or early initialization of heavy resources (e.g. TokenSet, Regex, etc.) when the extension class is loaded. Note, that even declarations marked with '@JvmStatic' still produce an extra class for the companion object, potentially causing expensive computations. Instead of being stored in companion object, these declarations must be top-level or stored in an object. FAQ How to rewrite run ConfigurationType? Move the declaration to top-level: '// DO, use top level fun\n internal fun mnRunConfigurationType(): MnRunConfigurationType = runConfigurationType()\n\n internal class MnRunConfigurationType : ConfigurationType {\n companion object { // DON'T\n fun getInstance(): MnRunConfigurationType = runConfigurationType()\n }\n ...' How to rewrite FileType? Before: 'internal class SpringBootImportsFileType : LanguageFileType(SPILanguage.INSTANCE, true) {\n companion object {\n val FILE_TYPE = SpringBootImportsFileType()\n ...' After: 'internal object SpringBootImportsFileType : LanguageFileType(SPILanguage.INSTANCE, true) {\n ...' Use 'INSTANCE' fieldName in 'plugin.xml': '' How to rewrite CounterUsagesCollector? Internal API Before: 'class AntActionsUsagesCollector : CounterUsagesCollector() {\n override fun getGroup(): EventLogGroup = GROUP\n\n companion object {\n private val GROUP = EventLogGroup(\"build.ant.actions\", 1)\n\n @JvmField\n val runSelectedBuildAction = GROUP.registerEvent(\"RunSelectedBuild\")\n }\n}' After: 'object AntActionsUsagesCollector : CounterUsagesCollector() {\n override fun getGroup(): EventLogGroup = GROUP\n\n private val GROUP = EventLogGroup(\"build.ant.actions\", 1)\n\n @JvmField\n val runSelectedBuildAction = GROUP.registerEvent(\"RunSelectedBuild\")\n}' New in 2023.3", + "markdown": "Reports incorrect companion objects' usage in [extensions](https://plugins.jetbrains.com/docs/intellij/plugin-extensions.html).\n\n\nKotlin companion object is always created once you try to load its containing class, and extension point implementations are supposed to be cheap to create.\nExcessive classloading in plugins is a huge problem for IDE startup.\n\nBad pattern:\n\n\n class KotlinDocumentationProvider : AbstractDocumentationProvider(), ExternalDocumentationProvider {\n\n companion object {\n private val LOG = Logger.getInstance(KotlinDocumentationProvider::class.java)\n private val javaDocumentProvider = JavaDocumentationProvider()\n }\n }\n\n\nHere `KotlinDocumentationProvider` is an extension registered in `plugin.xml`:\n\n\n \n\n\nIn this example `JavaDocumentationProvider` will be loaded from disk once someone just calls `new KotlinDocumentationProvider()`.\n\n\nKotlin companion objects in extension point implementation can only contain a logger and simple constants.\nOther declarations may cause excessive classloading or early initialization of heavy resources (e.g. TokenSet, Regex, etc.)\nwhen the extension class is loaded.\nNote, that even declarations marked with `@JvmStatic` still produce an extra class for the companion object, potentially causing expensive computations.\n\n\nInstead of being stored in companion object, these declarations must be top-level or stored in an object.\n\n### FAQ\n\n#### How to rewrite run ConfigurationType?\n\nMove the declaration to top-level:\n\n\n // DO, use top level fun\n internal fun mnRunConfigurationType(): MnRunConfigurationType = runConfigurationType()\n\n internal class MnRunConfigurationType : ConfigurationType {\n companion object { // DON'T\n fun getInstance(): MnRunConfigurationType = runConfigurationType()\n }\n ...\n\n#### How to rewrite FileType?\n\nBefore:\n\n\n internal class SpringBootImportsFileType : LanguageFileType(SPILanguage.INSTANCE, true) {\n companion object {\n val FILE_TYPE = SpringBootImportsFileType()\n ...\n\nAfter:\n\n\n internal object SpringBootImportsFileType : LanguageFileType(SPILanguage.INSTANCE, true) {\n ...\n\nUse `INSTANCE` fieldName in `plugin.xml`:\n\n\n \n\n#### How to rewrite CounterUsagesCollector?\n\n##### Internal API\n\nBefore:\n\n\n class AntActionsUsagesCollector : CounterUsagesCollector() {\n override fun getGroup(): EventLogGroup = GROUP\n\n companion object {\n private val GROUP = EventLogGroup(\"build.ant.actions\", 1)\n\n @JvmField\n val runSelectedBuildAction = GROUP.registerEvent(\"RunSelectedBuild\")\n }\n }\n\nAfter:\n\n\n object AntActionsUsagesCollector : CounterUsagesCollector() {\n override fun getGroup(): EventLogGroup = GROUP\n\n private val GROUP = EventLogGroup(\"build.ant.actions\", 1)\n\n @JvmField\n val runSelectedBuildAction = GROUP.registerEvent(\"RunSelectedBuild\")\n }\n\nNew in 2023.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "CompanionObjectInExtension", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UsePlatformProcessAwaitExit", + "shortDescription": { + "text": "Use 'com.intellij.util.io.ProcessKt.awaitExit()'" + }, + "fullDescription": { + "text": "Reports usages of 'Process.waitFor()' and 'Process.onExit()' in coroutine context. Using these methods in coroutine context is forbidden. Use 'com.intellij.util.io.awaitExit()' instead. Example: 'suspend fun doSomething(process: Process) {\n val exitCode = process.waitFor() // bad\n // ...\n}\n\nsuspend fun doSomething(process: Process) {\n val exitCode = process.awaitExit() // good\n // ...\n}' New in 2023.3", + "markdown": "Reports usages of `Process.waitFor()` and `Process.onExit()` in coroutine context. Using these methods in coroutine context is forbidden.\n\nUse `com.intellij.util.io.awaitExit()` instead.\n\nExample:\n\n\n suspend fun doSomething(process: Process) {\n val exitCode = process.waitFor() // bad\n // ...\n }\n\n suspend fun doSomething(process: Process) {\n val exitCode = process.awaitExit() // good\n // ...\n }\n\nNew in 2023.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "UsePlatformProcessAwaitExit", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnknownIdInMetaInformation", + "shortDescription": { + "text": "Unknown inspection id in meta information" + }, + "fullDescription": { + "text": "Reports unknown inspection ids in 'metaInformation.json' files. It contains additional information about inspections declared in the current plugin. The presence of inspections in this file is optional. An example of such information is a list of corresponding CWE IDs. Entries in 'metaInformation.json' with unknown inspection ID will be ignored at runtime. The presence of such elements could be a bug or outdated information.", + "markdown": "Reports unknown inspection ids in `metaInformation.json` files. It contains additional information about inspections declared in the current plugin. The presence of inspections in this file is optional. An example of such information is a list of corresponding CWE IDs. Entries in `metaInformation.json` with unknown inspection ID will be ignored at runtime. The presence of such elements could be a bug or outdated information." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "UnknownIdInMetaInformation", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit", + "index": 41, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SerializableCtor", + "shortDescription": { + "text": "Non-default constructor in serializable class" + }, + "fullDescription": { + "text": "Reports non-default constructor in serializable classes. The platform's 'IonObjectSerializer' requires specifying '@PropertyMapping' explicitly. Quick-fix generates necessary '@PropertyMapping' annotation for the constructor.", + "markdown": "Reports non-default constructor in serializable classes.\n\n\nThe platform's `IonObjectSerializer` requires specifying `@PropertyMapping` explicitly.\n\n\nQuick-fix generates necessary `@PropertyMapping` annotation for the constructor." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "SerializableCtor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PsiElementConcatenation", + "shortDescription": { + "text": "Using PsiElement string representation to generate new expression is incorrect" + }, + "fullDescription": { + "text": "Reports direct usage of 'PsiElement' and 'PsiType' in strings. When building strings for 'PsiJavaParserFacade.createExpressionFromText()' (or similar methods), 'PsiElement.getText()' should be used instead.", + "markdown": "Reports direct usage of `PsiElement` and `PsiType` in strings.\n\n\nWhen building strings for `PsiJavaParserFacade.createExpressionFromText()` (or similar methods), `PsiElement.getText()` should be used\ninstead." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PsiElementConcatenation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ComponentNotRegistered", + "shortDescription": { + "text": "Component/Action not registered" + }, + "fullDescription": { + "text": "Reports plugin components and actions that are not registered in a 'plugin.xml' descriptor. This eases developing new components when using the \"Create Class\" intention and helps keep track of potentially obsolete components. Provided quick-fix to register the component adds necessary registration in 'plugin.xml' descriptor. Configure the inspection: Use the Check Actions option to turn off the check for Actions, as they may be intentionally created and registered dynamically. Use the Ignore non-public classes option to ignore abstract and non-public classes.", + "markdown": "Reports plugin components and actions that are not registered in a `plugin.xml` descriptor.\n\n\nThis eases developing new components when using the \"Create Class\" intention and helps keep track of potentially obsolete components.\n\n\nProvided quick-fix to register the component adds necessary registration in `plugin.xml` descriptor.\n\nConfigure the inspection:\n\n* Use the **Check Actions** option to turn off the check for Actions, as they may be intentionally created and registered dynamically.\n* Use the **Ignore non-public classes** option to ignore abstract and non-public classes." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ComponentNotRegistered", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DevKitPropertiesMessageValidation", + "shortDescription": { + "text": "Message format validation in properties files" + }, + "fullDescription": { + "text": "Reports the following 'MessageFormat' problems in property values: Unknown format types 'MessageFormat' supports only these format types: number date time choice Other format types will be reported. Unpaired quotes 'property.key=Shouldn't happen: {0}' A single quote is interpreted as an escape until the end of the property, and will not be present in the result string. In most cases this is not what is intended. If a single quote should be present in the result string, it has to be duplicated in the property. Unmatched braces Every placeholder must have a closing brace. Too many quotes In some cases (e.g. 'it's'), it is expected that only one quote ends up in the result string. Cases where two or more quotes are placed together in the result string are reported. Incorrect lower bounds for nested 'ChoiceFormat' Lower bounds are expected to be numbers and to be sorted in ascending order. Wrong number of quotes around parameters In 'java.text.MessageFormat' patterns single quotes are used for escaping. To keep quotes visible, they must be duplicated. For example when passing '1': ''{0}'' → '{0}' '''{0}''' → ''1'' When using choice format, nested formats are evaluated as format strings themselves, and quotes will need to be duplicated twice. For example when passing '1': '{0, choice, 0#no|#1''{0}'' files}' → '{0} files' '{0, choice, 0#no|#1''''{0}'''' files}' → ''1' files' Note Property values are verified only if they contain the literal text '{0}', '{0,', '{1}' or '{1,'. This is to make sure that these property values are actually used as 'MessageFormat' patterns. New in 2023.2", + "markdown": "Reports the following `MessageFormat` problems in property values:\n\n**Unknown format types**\n\n\n`MessageFormat` supports only these format types:\n\n* number\n* date\n* time\n* choice\n\nOther format types will be reported.\n\n**Unpaired quotes**\n\n\n property.key=Shouldn't happen: {0}\n\n\nA single quote is interpreted as an escape until the end of the property, and will not be present in the result string.\nIn most cases this is not what is intended.\nIf a single quote should be present in the result string, it has to be duplicated in the property.\n\n**Unmatched braces**\n\n\nEvery placeholder must have a closing brace.\n\n**Too many quotes**\n\n\nIn some cases (e.g. `it's`), it is expected that only one quote ends up in the result string.\nCases where two or more quotes are placed together in the result string are reported.\n\n**Incorrect lower bounds for nested `ChoiceFormat`**\n\n\nLower bounds are expected to be numbers and to be sorted in ascending order.\n\n**Wrong number of quotes around parameters**\n\n\nIn `java.text.MessageFormat` patterns single quotes are used for escaping.\nTo keep quotes visible, they must be duplicated.\nFor example when passing `1`:\n\n* `'{0}'` → `{0}`\n* `''{0}''` → `'1'`\n\n\nWhen using choice format, nested formats are evaluated as format strings themselves, and quotes will need to be duplicated twice.\nFor example when passing `1`:\n\n* `{0, choice, 0#no|#1''{0}'' files}` → `{0} files`\n* `{0, choice, 0#no|#1''''{0}'''' files}` → `'1' files`\n\n**Note**\n\n\nProperty values are verified only if they contain the literal text `{0}`, `{0,`, `{1}` or `{1,`.\nThis is to make sure that these property values are actually used as `MessageFormat` patterns.\n\nNew in 2023.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "DevKitPropertiesMessageValidation", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit", + "index": 41, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnsafeReturnStatementVisitor", + "shortDescription": { + "text": "Unsafe return statements visitor" + }, + "fullDescription": { + "text": "Reports unsafe use of 'JavaRecursiveElementVisitor.visitReturnStatement()'. Processing 'PsiReturnStatement's even if they belong to another 'PsiClass' or 'PsiLambdaExpression' is a bug in most cases, and a visitor most probably should implement 'visitClass()' and 'visitLambdaExpression()' methods.", + "markdown": "Reports unsafe use of `JavaRecursiveElementVisitor.visitReturnStatement()`.\n\n\nProcessing `PsiReturnStatement`s\neven if they belong to another `PsiClass` or `PsiLambdaExpression` is a bug in most cases, and a visitor most\nprobably should implement `visitClass()` and `visitLambdaExpression()` methods." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnsafeReturnStatementVisitor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnresolvedPluginConfigReference", + "shortDescription": { + "text": "Unresolved plugin configuration reference" + }, + "fullDescription": { + "text": "Reports unresolved references to plugin configuration elements. Extensions Referencing extension with an unknown 'id' might result in errors at runtime. The following extension points are supported: 'com.intellij.advancedSetting' in resource bundle 'advanced.setting.*' key 'com.intellij.experimentalFeature' in 'Experiments.isFeatureEnabled()/setFeatureEnabled()' 'com.intellij.notificationGroup' in 'Notification' constructor and 'NotificationGroupManager.getNotificationGroup()' 'com.intellij.registryKey' in 'Registry' methods 'key' parameter 'com.intellij.toolWindow' in resource bundle 'toolwindow.stripe.*' key Extension Point Extension point name referencing its corresponding '' declaration in 'plugin.xml'. 'com.intellij.openapi.extensions.ExtensionPointName' constructor and 'create()' 'com.intellij.openapi.extensions.ProjectExtensionPointName' constructor 'com.intellij.openapi.util.KeyedExtensionCollector' and inheritors constructor", + "markdown": "Reports unresolved references to plugin configuration elements.\n\n#### Extensions\n\n\nReferencing extension with an unknown `id` might result in errors at runtime.\n\n\nThe following extension points are supported:\n\n* `com.intellij.advancedSetting` in resource bundle `advanced.setting.*` key\n* `com.intellij.experimentalFeature` in `Experiments.isFeatureEnabled()/setFeatureEnabled()`\n* `com.intellij.notificationGroup` in `Notification` constructor and `NotificationGroupManager.getNotificationGroup()`\n* `com.intellij.registryKey` in `Registry` methods `key` parameter\n* `com.intellij.toolWindow` in resource bundle `toolwindow.stripe.*` key\n\n#### Extension Point\n\n\nExtension point name referencing its corresponding `` declaration in `plugin.xml`.\n\n* `com.intellij.openapi.extensions.ExtensionPointName` constructor and `create()`\n* `com.intellij.openapi.extensions.ProjectExtensionPointName` constructor\n* `com.intellij.openapi.util.KeyedExtensionCollector` and inheritors constructor" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "UnresolvedPluginConfigReference", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UseDPIAwareInsets", + "shortDescription": { + "text": "Use DPI-aware insets" + }, + "fullDescription": { + "text": "Reports usages of 'java.awt.Insets' and 'JBUI.insetsXyz()' that can be simplified. The 'Insets' instances are not DPI-aware and can result in UI layout problems. Quick fix performs replacement with 'JBUI.insets()' or simplifies the expression. Example: '// bad:\nInsets insets1 = new Insets(1, 2, 3, 4);\nInsets insets2 = new Insets(1, 2, 1, 2);\nInsets insets3 = new Insets(1, 0, 0, 0);\n\n// good:\nInsets insets1 = JBUI.insets(1, 2, 3, 4);\nInsets insets2 = JBUI.insets(1, 2);\nInsets insets3 = JBUI.insetsTop(1);'", + "markdown": "Reports usages of `java.awt.Insets` and `JBUI.insetsXyz()` that can be simplified.\n\n\nThe `Insets` instances are not DPI-aware and can result in UI layout problems.\n\n\nQuick fix performs replacement with `JBUI.insets()` or simplifies the expression.\n\nExample:\n\n\n // bad:\n Insets insets1 = new Insets(1, 2, 3, 4);\n Insets insets2 = new Insets(1, 2, 1, 2);\n Insets insets3 = new Insets(1, 0, 0, 0);\n\n // good:\n Insets insets1 = JBUI.insets(1, 2, 3, 4);\n Insets insets2 = JBUI.insets(1, 2);\n Insets insets3 = JBUI.insetsTop(1);\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UseDPIAwareInsets", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ThreadingConcurrency", + "shortDescription": { + "text": "Threading and concurrency problems" + }, + "fullDescription": { + "text": "Reports threading and concurrency issues in code using information from 'com.intellij.util.concurrency.annotations' annotations. Work in progress. Enable options below to check '@Requires(Read|Write)Lock' inside '@RequiresEdt' blocks. Additionally, non-overriding public methods having not all annotations present can be highlighted + quickfix to add missing annotations. New in 2023.2", + "markdown": "Reports threading and concurrency issues in code using information from `com.intellij.util.concurrency.annotations` annotations.\n\n\n**Work in progress**.\nEnable options below to check `@Requires(Read|Write)Lock` inside `@RequiresEdt` blocks. Additionally, non-overriding public methods having not all annotations present can be highlighted + quickfix to add missing annotations.\n\nNew in 2023.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "ThreadingConcurrency", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UndesirableClassUsage", + "shortDescription": { + "text": "Undesirable class usage" + }, + "fullDescription": { + "text": "Reports usages of undesirable classes (mostly Swing components). Quick-fix offers replacement with recommended IntelliJ Platform replacement.", + "markdown": "Reports usages of undesirable classes (mostly Swing components).\n\n\nQuick-fix offers replacement with recommended IntelliJ Platform replacement." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UndesirableClassUsage", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "CallingMethodShouldBeRequiresBlockingContext", + "shortDescription": { + "text": "Calling method should be annotated with @RequiresBlockingContext" + }, + "fullDescription": { + "text": "Highlights calls of method annotated with '@RequiresBlockingMethod' inside non-annotated method. The calling method should be also annotated with '@RequiresBlockingMethod' to clearly state its contract.", + "markdown": "Highlights calls of method annotated with `@RequiresBlockingMethod` inside non-annotated method.\nThe calling method should be also annotated with `@RequiresBlockingMethod` to clearly state its contract." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "CallingMethodShouldBeRequiresBlockingContext", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InspectionDescriptionNotFoundInspection", + "shortDescription": { + "text": "Inspection description checker" + }, + "fullDescription": { + "text": "Reports inspections that are missing an HTML description file, i.e. a file containing a text like this. The Create description file quick-fix creates a template HTML description file.", + "markdown": "Reports inspections that are missing an HTML description file, i.e. a file containing a text like this.\n\n\nThe **Create description file** quick-fix creates a template HTML description file." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "InspectionDescriptionNotFoundInspection", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Description file", + "index": 104, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PluginXmlValidity", + "shortDescription": { + "text": "Plugin.xml validity" + }, + "fullDescription": { + "text": "Reports problems in 'plugin.xml'. Invalid configuration can lead to problems at runtime.", + "markdown": "Reports problems in `plugin.xml`.\n\n\nInvalid configuration can lead to problems at runtime." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "PluginXmlValidity", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Plugin descriptor", + "index": 76, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LeakableMapKey", + "shortDescription": { + "text": "Map key may leak" + }, + "fullDescription": { + "text": "Reports using 'Language' or 'FileType' as a map key in plugin code. Such usages might lead to inability to unload the plugin properly. Please consider using 'String' as keys instead. See Dynamic Plugins in SDK Docs for more information.", + "markdown": "Reports using `Language` or `FileType` as a map key in plugin code.\n\n\nSuch usages might lead to inability to unload the plugin properly.\n\n\nPlease consider using `String` as keys instead.\n\n\nSee [Dynamic\nPlugins](https://plugins.jetbrains.com/docs/intellij/dynamic-plugins.html) in SDK Docs for more information." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LeakableMapKey", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "PostfixTemplateDescriptionNotFound", + "shortDescription": { + "text": "Postfix template description checker" + }, + "fullDescription": { + "text": "Reports postfix templates missing an HTML description file, 'before.template' file or 'after.template' file. These are shown in Settings | Editor | General | Postfix Completion. The Create description file quick-fix creates a template HTML description file.", + "markdown": "Reports postfix templates missing an HTML description file, `before.template` file or `after.template` file. These are shown in [Settings \\| Editor \\| General \\| Postfix Completion](settings://reference.settingsdialog.IDE.editor.postfix.templates).\n\n\nThe **Create description file** quick-fix creates a template HTML description file." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "PostfixTemplateDescriptionNotFound", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Description file", + "index": 104, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnsafeVfsRecursion", + "shortDescription": { + "text": "Unsafe VFS recursion" + }, + "fullDescription": { + "text": "Reports usage of 'VirtualFile.getChildren()' inside recursive methods. This may cause endless loops when iterating over cyclic symlinks. Use 'VfsUtilCore.visitChildrenRecursively()' instead. 'void processDirectory(VirtualFile dir) {\n for (VirtualFile file : dir.getChildren()) { // bad\n if (!file.isDirectory()) {\n processFile(file);\n } else {\n processDirectory(file); // recursive call\n }\n }\n}'\n 'void processDirectory(VirtualFile dir) {\n VfsUtilCore.visitChildrenRecursively(dir, new VirtualFileVisitor() { // good\n @Override\n public boolean visitFile(@NotNull VirtualFile file) {\n if (!file.isDirectory()) {\n processFile(file);\n }\n return true;\n }\n });\n}'", + "markdown": "Reports usage of `VirtualFile.getChildren()` inside recursive methods.\n\n\nThis may cause endless loops when iterating over cyclic symlinks.\nUse `VfsUtilCore.visitChildrenRecursively()` instead.\n\n\n void processDirectory(VirtualFile dir) {\n for (VirtualFile file : dir.getChildren()) { // bad\n if (!file.isDirectory()) {\n processFile(file);\n } else {\n processDirectory(file); // recursive call\n }\n }\n }\n\n\n void processDirectory(VirtualFile dir) {\n VfsUtilCore.visitChildrenRecursively(dir, new VirtualFileVisitor() { // good\n @Override\n public boolean visitFile(@NotNull VirtualFile file) {\n if (!file.isDirectory()) {\n processFile(file);\n }\n return true;\n }\n });\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnsafeVfsRecursion", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ListenerImplementationMustNotBeDisposable", + "shortDescription": { + "text": "Listener implementation implements 'Disposable'" + }, + "fullDescription": { + "text": "Reports listener implementations that implement 'com.intellij.openapi.Disposable'. Listener implementations must be stateless and must not implement life-cycle, including 'com.intellij.openapi.Disposable'. See documentation for more information. New in 2023.3", + "markdown": "Reports listener implementations that implement `com.intellij.openapi.Disposable`.\n\n\nListener implementations must be stateless and must not implement life-cycle, including `com.intellij.openapi.Disposable`.\n\n\nSee [documentation](https://plugins.jetbrains.com/docs/intellij/plugin-listeners.html) for more information.\n\nNew in 2023.3" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "ListenerImplementationMustNotBeDisposable", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "KotlinObjectRegisteredAsExtension", + "shortDescription": { + "text": "Kotlin object registered as extension" + }, + "fullDescription": { + "text": "Reports Kotlin objects that are registered as plugin extensions. Extensions lifecycle is managed by the IntelliJ Platform. Using Kotlin objects for extension registration may cause creation of unnecessary extension instances and make plugin unloading impossible. Example Extension registration: '' Extension implementation: '// bad:\nobject MyAnnotator : Annotator {\n ...\n}\n\n// good:\nclass MyAnnotator : Annotator {\n ...\n}' New in 2023.1", + "markdown": "Reports Kotlin objects that are registered as plugin extensions.\n\n\nExtensions lifecycle is managed by the IntelliJ Platform.\nUsing Kotlin objects for extension registration may cause creation of unnecessary extension instances and make plugin unloading\nimpossible.\n\nExample\n-------\n\nExtension registration:\n\n\n \n\nExtension implementation:\n\n\n // bad:\n object MyAnnotator : Annotator {\n ...\n }\n\n // good:\n class MyAnnotator : Annotator {\n ...\n }\n\nNew in 2023.1" + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "KotlinObjectRegisteredAsExtension", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FileEqualsUsage", + "shortDescription": { + "text": "File.equals() usage" + }, + "fullDescription": { + "text": "Reports usages of 'java.io.File.equals()/hashCode()/compareTo()' methods. These do not honor case-insensitivity on macOS. Use 'com.intellij.openapi.util.io.FileUtil.filesEquals()/fileHashCode()/compareFiles()' methods instead.", + "markdown": "Reports usages of `java.io.File.equals()/hashCode()/compareTo()` methods.\n\n\nThese do not honor case-insensitivity on macOS.\n\n\nUse `com.intellij.openapi.util.io.FileUtil.filesEquals()/fileHashCode()/compareFiles()` methods instead." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "FileEqualsUsage", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Code", + "index": 42, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "WorkspaceImplAbsent", + "shortDescription": { + "text": "Absent entity implementation" + }, + "fullDescription": { + "text": "Reports absent of implementation for the entity. Verifies that each entity in the project has the implementation. Suggests generation implementation for the whole entities in the current module.", + "markdown": "Reports absent of implementation for the entity.\n\n\nVerifies that each entity in the project has the implementation.\n\n\nSuggests generation implementation for the whole entities in the current module." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "WorkspaceImplAbsent", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Plugin DevKit/Workspace model", + "index": 56, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + } + ], + "language": "en-US", + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "isComprehensive": false + }, + { + "name": "Lombook Plugin", + "version": "233.14714", + "rules": [ + { + "id": "LombokGetterMayBeUsed", + "shortDescription": { + "text": "Lombok @Getter may be used" + }, + "fullDescription": { + "text": "Reports standard getter method that can be replaced by the lombok '@Getter' annotation. Example: 'import java.util.Date;\n\n public class MyClass {\n /**\n * The process date.\n */\n private Date processDate;\n\n /**\n * Returns the date.\n *\n * @return The date\n */\n public Date getProcessDate() {\n return processDate;\n }\n }' After the quick-fix/cleanup is applied: 'import lombok.Getter;\n import java.util.Date;\n\n @Getter\n public class MyClass {\n /**\n * The process date.\n * -- GETTER --\n * Returns the date.\n *\n * @return The date\n */\n private Date processDate;\n }' It only reports when the lombok library is configured. To unlombok, see the lombok site. New in 2023.2", + "markdown": "Reports standard getter method that can be replaced by the lombok `@Getter` annotation.\n\nExample:\n\n\n import java.util.Date;\n\n public class MyClass {\n /**\n * The process date.\n */\n private Date processDate;\n\n /**\n * Returns the date.\n *\n * @return The date\n */\n public Date getProcessDate() {\n return processDate;\n }\n }\n\nAfter the quick-fix/cleanup is applied:\n\n\n import lombok.Getter;\n import java.util.Date;\n\n @Getter\n public class MyClass {\n /**\n * The process date.\n * -- GETTER --\n * Returns the date.\n *\n * @return The date\n */\n private Date processDate;\n }\n\nIt only reports when the lombok library is configured. To unlombok, see the lombok site.\n\nNew in 2023.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LombokGetterMayBeUsed", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantSlf4jDefinition", + "shortDescription": { + "text": "@Slf4j" + }, + "fullDescription": { + "text": "Reports explicitly defined Slf4j Loggers. The '@Slf4j' annotation can be used instead.", + "markdown": "Reports explicitly defined *Slf4j* Loggers. The `@Slf4j` annotation can be used instead." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantSlf4jDefinition", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Lombok/Redundant definitions", + "index": 59, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantModifiersUtilityClassLombok", + "shortDescription": { + "text": "@UtilityClass modifiers" + }, + "fullDescription": { + "text": "Reports unneeded modifiers for classes annotated with '@UtilityClass'.", + "markdown": "Reports unneeded modifiers for classes annotated with `@UtilityClass`." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantModifiersUtilityClassLombok", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Lombok/Redundant modifiers", + "index": 86, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantModifiersValueLombok", + "shortDescription": { + "text": "@Value modifiers" + }, + "fullDescription": { + "text": "Reports unneeded modifiers for classes annotated with '@Value'.", + "markdown": "Reports unneeded modifiers for classes annotated with `@Value`." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantModifiersValueLombok", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Lombok/Redundant modifiers", + "index": 86, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "LombokSetterMayBeUsed", + "shortDescription": { + "text": "Lombok @Setter may be used" + }, + "fullDescription": { + "text": "Reports standard setter method that can be replaced by the lombok '@Setter' annotation. Example: 'import java.util.Date;\n\n public class MyClass {\n /**\n * The process date.\n */\n private Date processDate;\n\n /**\n * Sets the date.\n *\n * @param The date\n */\n public void setProcessDate(Date param) {\n processDate = param;\n }\n }' After the quick-fix/cleanup is applied: 'import lombok.Setter;\n import java.util.Date;\n\n @Setter\n public class MyClass {\n /**\n * The process date.\n * -- SETTER --\n * Sets the date.\n *\n * @param The date\n */\n private Date processDate;\n }' It only reports when the lombok library is configured. To unlombok, see the lombok site. New in 2023.2", + "markdown": "Reports standard setter method that can be replaced by the lombok `@Setter` annotation.\n\nExample:\n\n\n import java.util.Date;\n\n public class MyClass {\n /**\n * The process date.\n */\n private Date processDate;\n\n /**\n * Sets the date.\n *\n * @param The date\n */\n public void setProcessDate(Date param) {\n processDate = param;\n }\n }\n\nAfter the quick-fix/cleanup is applied:\n\n\n import lombok.Setter;\n import java.util.Date;\n\n @Setter\n public class MyClass {\n /**\n * The process date.\n * -- SETTER --\n * Sets the date.\n *\n * @param The date\n */\n private Date processDate;\n }\n\nIt only reports when the lombok library is configured. To unlombok, see the lombok site.\n\nNew in 2023.2" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "LombokSetterMayBeUsed", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Verbose or redundant code constructs", + "index": 37, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "SpringQualifierCopyableLombok", + "shortDescription": { + "text": "@Qualifier not copyable by lombok" + }, + "fullDescription": { + "text": "Reports Spring '@Qualifier' annotations on class fields that are ignored by the corresponding Lombok '@RequiredArgsConstructor' and '@AllArgsConstructor' annotations. The generated constructors will not receive the '@Qualifier' annotation without a 'lombok.copyableAnnotations' definition inside the 'lombok.config' file.", + "markdown": "Reports Spring `@Qualifier` annotations on class fields that are ignored by the corresponding Lombok `@RequiredArgsConstructor` and `@AllArgsConstructor` annotations. The generated constructors will not receive the `@Qualifier` annotation without a `lombok.copyableAnnotations` definition inside the `lombok.config` file." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "SpringQualifierCopyableLombok", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Lombok", + "index": 58, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "Lombok", + "shortDescription": { + "text": "Lombok annotations" + }, + "fullDescription": { + "text": "Offers general inspections for Lombok annotations.", + "markdown": "Offers general inspections for Lombok annotations." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "Lombok", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Lombok", + "index": 58, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "RedundantModifiersValLombok", + "shortDescription": { + "text": "Unnecessary final before 'val'" + }, + "fullDescription": { + "text": "Reports unneeded 'final' modifiers before 'val'.", + "markdown": "Reports unneeded `final` modifiers before `val`." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "RedundantModifiersValLombok", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Lombok/Redundant modifiers", + "index": 86, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DeprecatedLombok", + "shortDescription": { + "text": "Deprecated Lombok annotations" + }, + "fullDescription": { + "text": "Reports deprecated Lombok annotations and suggests quick-fixes to replace them with the ones promoted to the main package.", + "markdown": "Reports deprecated Lombok annotations and suggests quick-fixes to replace them with the ones promoted to the main package." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "DeprecatedLombok", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/Lombok", + "index": 58, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + } + ], + "language": "en-US", + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "isComprehensive": false + }, + { + "name": "TestNG-J", + "version": "233.14714", + "rules": [ + { + "id": "groupsTestNG", + "shortDescription": { + "text": "Undefined group name" + }, + "fullDescription": { + "text": "Reports undefined group names passed to the 'dependsOnGroups' or 'groups' attributes in the '@Test' annotation. The quick-fix adds an undefined name to a list of known groups. Use the Defined Groups field to define a comma-separated list of known groups.", + "markdown": "Reports undefined group names passed to the `dependsOnGroups` or `groups` attributes in the `@Test` annotation.\n\nThe quick-fix adds an undefined name to a list of known groups.\n\nUse the **Defined Groups** field to define a comma-separated list of known groups." + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "groupsTestNG", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/TestNG", + "index": 75, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "JUnitTestNG", + "shortDescription": { + "text": "JUnit Test can be converted to TestNG" + }, + "fullDescription": { + "text": "Reports any JUnit-based test class that can be converted into TestNG based unit test. Example: 'import org.junit.Test;\nimport static org.junit.Assert.*;\n\npublic class ExampleTest {\n @Test\n public void testExample(){\n assertEquals(2 + 2, 4);\n }\n}' After the quick-fix is applied: 'import org.testng.Assert;\nimport org.testng.annotations.Test;\n\npublic class ExampleTest {\n @Test\n public void testExample(){\n Assert.assertEquals(4, 2 + 2);\n }\n}'", + "markdown": "Reports any JUnit-based test class that can be converted into TestNG based unit test.\n\nExample:\n\n\n import org.junit.Test;\n import static org.junit.Assert.*;\n\n public class ExampleTest {\n @Test\n public void testExample(){\n assertEquals(2 + 2, 4);\n }\n }\n\nAfter the quick-fix is applied:\n\n\n import org.testng.Assert;\n import org.testng.annotations.Test;\n\n public class ExampleTest {\n @Test\n public void testExample(){\n Assert.assertEquals(4, 2 + 2);\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "JUnitTestNG", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/TestNG", + "index": 75, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConvertJavadoc", + "shortDescription": { + "text": "TestNG Javadoc can be converted to annotations" + }, + "fullDescription": { + "text": "Asserts your TestNG tests with Javadoc annotations and converts them to JDK annotations. Example: '/**\n * @testng.before-test\n */\n public void sample() {}' After the quick-fix is applied: '@BeforeTest\n public void sample() {}'", + "markdown": "Asserts your TestNG tests with Javadoc annotations and converts them to JDK annotations.\n\nExample:\n\n\n /**\n * @testng.before-test\n */\n public void sample() {}\n\nAfter the quick-fix is applied:\n\n\n @BeforeTest\n public void sample() {}\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ConvertJavadoc", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/TestNG", + "index": 75, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DataProviderReturnType", + "shortDescription": { + "text": "Invalid data provider return type" + }, + "fullDescription": { + "text": "Reports methods marked with '@DataProvider' annotation that doesn't return 'Object[][]' or 'Iterator'. If another type is returned, TestNG throws an exception. Example: 'public class TestNgTest {\n @DataProvider(name = \"Languages\")\n List getData() {\n return List.of(\"Java\", \"Kotlin\");\n }\n\n @Test(dataProvider = \"Languages\")\n public void testData(String language) {\n System.out.println(language);\n }\n}'", + "markdown": "Reports methods marked with `@DataProvider` annotation that doesn't return `Object[][]` or `Iterator`. If another type is returned, TestNG throws an exception.\n\nExample:\n\n\n public class TestNgTest {\n @DataProvider(name = \"Languages\")\n List getData() {\n return List.of(\"Java\", \"Kotlin\");\n }\n\n @Test(dataProvider = \"Languages\")\n public void testData(String language) {\n System.out.println(language);\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "error", + "parameters": { + "suppressToolId": "DataProviderReturnType", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Java/TestNG", + "index": 75, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ExpectedExceptionNeverThrownTestNG", + "shortDescription": { + "text": "Expected exception never thrown in test method body" + }, + "fullDescription": { + "text": "Reports checked exceptions expected by a TestNG test method that are never thrown inside the method body. Example: '@Test(expectedExceptions = Exception.class) // warning: Expected 'Exception' never thrown\n public void testEngineIsRunning() {\n assertTrue(engine.isRunning());\n }'", + "markdown": "Reports checked exceptions expected by a TestNG test method that are never thrown inside the method body.\n\n**Example:**\n\n\n @Test(expectedExceptions = Exception.class) // warning: Expected 'Exception' never thrown\n public void testEngineIsRunning() {\n assertTrue(engine.isRunning());\n }\n \n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ExpectedExceptionNeverThrownTestNG", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/TestNG", + "index": 75, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UndeclaredTests", + "shortDescription": { + "text": "Undeclared test" + }, + "fullDescription": { + "text": "Reports test classes that are not registered in 'testing.xml'. This is often a mistake because such tests won't be executed.", + "markdown": "Reports test classes that are not registered in `testing.xml`. This is often a mistake because such tests won't be executed." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UndeclaredTests", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/TestNG", + "index": 75, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "ConvertOldAnnotations", + "shortDescription": { + "text": "Old TestNG annotation @Configuration is used" + }, + "fullDescription": { + "text": "Reports TestNG 'org.testng.annotations.Configuration' annotations. It's better to configure a test suite with the modern '@BeforeXXX'/'@AfterXXX' annotations. Example: 'public class Test {\n @Configuration(beforeSuite = true, afterTest = true)\n public void afterBefore(){\n }\n }' After the quick-fix is applied: 'public class Test {\n @AfterTest()\n @BeforeSuite()\n public void afterBefore(){\n }\n }'", + "markdown": "Reports TestNG `org.testng.annotations.Configuration` annotations.\n\nIt's better to configure a test suite with the modern `@BeforeXXX`/`@AfterXXX` annotations.\n\nExample:\n\n\n public class Test {\n @Configuration(beforeSuite = true, afterTest = true)\n public void afterBefore(){\n }\n }\n\nAfter the quick-fix is applied:\n\n\n public class Test {\n @AfterTest()\n @BeforeSuite()\n public void afterBefore(){\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "ConvertOldAnnotations", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/TestNG", + "index": 75, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DuplicatedDataProviderNames", + "shortDescription": { + "text": "Duplicated data provider names" + }, + "fullDescription": { + "text": "Reports 'TestNG' data providers with equal names if 'org.testng.TestNGException' has occurred. Example: 'public class DuplicatedDataProviders {\n @DataProvider\n public Object[][] intTestData() { // duplicate 1\n return new Integer[][]{\n new Integer[]{1, 1},\n };\n }\n\n @DataProvider(name = \"intTestData\")\n public Object[][] someTestData() { // duplicate 2\n return new Integer[][]{\n new Integer[]{1, 1},\n };\n }\n\n @Test(dataProvider = \"intTestData\")\n public void testIsOK(Integer key, Integer value) {\n assertEquals(key, value);\n }\n }'", + "markdown": "Reports `TestNG` data providers with equal names if `org.testng.TestNGException` has occurred.\n\nExample:\n\n\n public class DuplicatedDataProviders {\n @DataProvider\n public Object[][] intTestData() { // duplicate 1\n return new Integer[][]{\n new Integer[]{1, 1},\n };\n }\n\n @DataProvider(name = \"intTestData\")\n public Object[][] someTestData() { // duplicate 2\n return new Integer[][]{\n new Integer[]{1, 1},\n };\n }\n\n @Test(dataProvider = \"intTestData\")\n public void testIsOK(Integer key, Integer value) {\n assertEquals(key, value);\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "error", + "parameters": { + "suppressToolId": "DuplicatedDataProviderNames", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Java/TestNG", + "index": 75, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "dependsOnMethodTestNG", + "shortDescription": { + "text": "Illegal method name passed to 'dependsOnMethods'" + }, + "fullDescription": { + "text": "Reports illegal method names passed to the 'dependsOnMethods' attribute in the '@Test' annotation. A method name is considered illegal if it can't be resolved into a valid, accessible '@Test' annotated method in the current class or any of its parent classes. Example: 'public class SampleTest {\n @Test(dependsOnMethods = \"testSpellignError\")\n public void testSample() {}\n @Test\n public void testSpellingError(){}\n }'", + "markdown": "Reports illegal method names passed to the `dependsOnMethods` attribute in the `@Test` annotation.\n\nA method name is considered illegal if it can't be resolved into a valid, accessible `@Test` annotated method in the current class\nor any of its parent classes.\n\nExample:\n\n\n public class SampleTest {\n @Test(dependsOnMethods = \"testSpellignError\")\n public void testSample() {}\n @Test\n public void testSpellingError(){}\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "dependsOnMethodTestNG", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/TestNG", + "index": 75, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MalformedDataProvider", + "shortDescription": { + "text": "Data provider problems" + }, + "fullDescription": { + "text": "Reports references to data provider methods that do not exist or are not accessible. Example: 'public class InstanceDataProviderFromForeignClass {\n // method data() doesn't exist in class A\n @Test(dataProvider = \"data\", dataProviderClass = A.class)\n public void test() {\n }\n}\nclass A { }' After the quick-fix is applied: '//the needed data() method is created in class A\nclass A {\n @DataProvider\n public Object[][] data() {\n return new Object[][]{};\n }\n}'", + "markdown": "Reports references to data provider methods that do not exist or are not accessible.\n\nExample:\n\n\n public class InstanceDataProviderFromForeignClass {\n // method data() doesn't exist in class A\n @Test(dataProvider = \"data\", dataProviderClass = A.class)\n public void test() {\n }\n }\n class A { }\n\nAfter the quick-fix is applied:\n\n\n //the needed data() method is created in class A\n class A {\n @DataProvider\n public Object[][] data() {\n return new Object[][]{};\n }\n }\n" + }, + "defaultConfiguration": { + "enabled": true, + "level": "warning", + "parameters": { + "suppressToolId": "MalformedDataProvider", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Java/TestNG", + "index": 75, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + } + ], + "language": "en-US", + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "isComprehensive": false + }, + { + "name": "com.intellij.properties", + "version": "233.14714", + "rules": [ + { + "id": "UseEllipsisInPropertyInspection", + "shortDescription": { + "text": "Three dot characters instead of the ellipsis" + }, + "fullDescription": { + "text": "Reports three \"dot\" characters which are used instead of the ellipsis character for UTF-8 properties files.", + "markdown": "Reports three \"dot\" characters which are used instead of the ellipsis character for UTF-8 properties files." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UseEllipsisInPropertyInspection", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Properties files", + "index": 32, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "AlphaUnsortedPropertiesFile", + "shortDescription": { + "text": "Properties file or resource bundle is alphabetically unsorted" + }, + "fullDescription": { + "text": "Reports alphabetically unsorted resource bundles or .properties files.", + "markdown": "Reports alphabetically unsorted resource bundles or .properties files." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "AlphaUnsortedPropertiesFile", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Properties files", + "index": 32, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "TrailingSpacesInProperty", + "shortDescription": { + "text": "Trailing spaces in property" + }, + "fullDescription": { + "text": "Reports properties whose keys or values end with a whitespace.", + "markdown": "Reports properties whose keys or values end with a whitespace." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "TrailingSpacesInProperty", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Properties files", + "index": 32, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "UnusedProperty", + "shortDescription": { + "text": "Unused property" + }, + "fullDescription": { + "text": "Reports properties that are not referenced outside of the .properties file they are contained in.", + "markdown": "Reports properties that are not referenced outside of the .properties file they are contained in." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "UnusedProperty", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Properties files", + "index": 32, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "WrongPropertyKeyValueDelimiter", + "shortDescription": { + "text": "Property key/value delimiter doesn't match code style settings" + }, + "fullDescription": { + "text": "Reports properties in which key or value delimiters do not match code style settings.", + "markdown": "Reports properties in which key or value delimiters do not match code style settings." + }, + "defaultConfiguration": { + "enabled": false, + "level": "note", + "parameters": { + "suppressToolId": "WrongPropertyKeyValueDelimiter", + "ideaSeverity": "WEAK WARNING", + "qodanaSeverity": "Moderate" + } + }, + "relationships": [ + { + "target": { + "id": "Properties files", + "index": 32, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DuplicatePropertyInspection", + "shortDescription": { + "text": "Duplicate property" + }, + "fullDescription": { + "text": "Reports duplicate property keys with different values, duplicate keys, or duplicate property values. Example: 'property1=value;\nproperty2=value;' The Options list allows selecting the area in which the inspection should search for duplicates.", + "markdown": "Reports duplicate property keys with different values, duplicate keys, or duplicate property values.\n\nExample:\n\n\n property1=value;\n property2=value;\n\nThe **Options** list allows selecting the area in which the inspection should search for duplicates." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "DuplicatePropertyInspection", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "Properties files", + "index": 32, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + } + ], + "language": "en-US", + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "isComprehensive": false + }, + { + "name": "com.intellij.uiDesigner", + "version": "233.14714", + "rules": [ + { + "id": "NoLabelFor", + "shortDescription": { + "text": "No label for component" + }, + "fullDescription": { + "text": "Reports components that do not have any static text and do not have any label marked with 'setLabelFor' for this component. Components that do not have static text include edit fields and combo boxes. Such components cannot be activated with a keyboard shortcut. The quick-fix for this inspection allows you to automatically associate an adjacent label with the problematic component.", + "markdown": "Reports components that do not have any static text and do not have any label marked with `setLabelFor` for this component.\n\nComponents that do not have static text include edit fields and combo boxes.\nSuch components cannot be activated with a keyboard shortcut. The quick-fix for this inspection\nallows you to automatically associate an adjacent label with the problematic component." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NoLabelFor", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "UI form", + "index": 85, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "InvalidPropertyKeyForm", + "shortDescription": { + "text": "Invalid property key in a UI form" + }, + "fullDescription": { + "text": "Reports unresolved references to .properties files.", + "markdown": "Reports unresolved references to .properties files." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "InvalidPropertyKeyForm", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "UI form", + "index": 85, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "MissingMnemonic", + "shortDescription": { + "text": "Missing mnemonics" + }, + "fullDescription": { + "text": "Reports focusable components with the 'text' property or labels with the assigned 'labelFor' property that do not have a mnemonic character. The quick-fix assigns a unique mnemonic to such a component.", + "markdown": "Reports focusable components with the `text` property or labels with the assigned `labelFor` property that do not have a mnemonic character. The quick-fix assigns a unique mnemonic to such a component." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "MissingMnemonic", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "UI form", + "index": 85, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "OneButtonGroup", + "shortDescription": { + "text": "Button group with one button" + }, + "fullDescription": { + "text": "Reports 'ButtonGroup' instances that contain only one 'JRadioButton'.", + "markdown": "Reports `ButtonGroup` instances that contain only one `JRadioButton`." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "OneButtonGroup", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "UI form", + "index": 85, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NoScrollPane", + "shortDescription": { + "text": "Scrollable component not in JScrollPane" + }, + "fullDescription": { + "text": "Reports 'Scrollable' components, except for 'JTextField', that are not placed in 'JScrollPane'. The quick-fix surrounds the problematic component with a scroll pane.", + "markdown": "Reports `Scrollable` components, except for `JTextField`, that are not placed in `JScrollPane`. The quick-fix surrounds the problematic component with a scroll pane." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NoScrollPane", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "UI form", + "index": 85, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "FormSpellChecking", + "shortDescription": { + "text": "Typo in a UI form" + }, + "fullDescription": { + "text": "Reports typos and misspelling in your UI forms (for example, in a 'JLabel' text or 'JPanel' title) and fixes them with one click.", + "markdown": "Reports typos and misspelling in your UI forms (for example, in a `JLabel` text or `JPanel` title) and fixes them\nwith one click." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "FormSpellChecking", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "UI form", + "index": 85, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "DuplicateMnemonic", + "shortDescription": { + "text": "Duplicate mnemonics" + }, + "fullDescription": { + "text": "Reports components that have duplicated mnemonic characters. The quick-fix assigns a unique mnemonic character to each of the components.", + "markdown": "Reports components that have duplicated mnemonic characters.\n\nThe quick-fix assigns a unique mnemonic character to each of the components." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "DuplicateMnemonic", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "UI form", + "index": 85, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "I18nForm", + "shortDescription": { + "text": "Hardcoded string literal in a UI form" + }, + "fullDescription": { + "text": "Reports any instances of hardcoded strings in your UI forms. Hardcoded string literals are usually errors in an internationalized environment. This inspection does not report empty strings and strings consisting of only whitespace. The quick-fix transforms a string literal into a reference to a property in a resource bundle.", + "markdown": "Reports any instances of hardcoded strings in your UI forms.\n\nHardcoded string literals are usually errors in\nan internationalized environment.\nThis inspection does not report empty strings and strings consisting of only whitespace.\n\nThe quick-fix transforms a string literal\ninto a reference to a property in a resource bundle." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "I18nForm", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "UI form", + "index": 85, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "NoButtonGroup", + "shortDescription": { + "text": "Radio button not in a group" + }, + "fullDescription": { + "text": "Reports 'JRadioButton' components that are not placed in 'ButtonGroup'. A quick-fix is available to group radio buttons placed in adjacent grid cells.", + "markdown": "Reports `JRadioButton` components that are not placed in `ButtonGroup`. A quick-fix is available to group radio buttons placed in adjacent grid cells." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "NoButtonGroup", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "UI form", + "index": 85, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "BoundFieldAssignment", + "shortDescription": { + "text": "Assignment to UI-bound field" + }, + "fullDescription": { + "text": "Reports assignments to fields which are bound to components in UI Designer forms. Such assignments will cause the component setup code generated by UI Designer for such fields to be ignored.", + "markdown": "Reports assignments to fields which are bound to components in UI Designer forms.\n\nSuch assignments will cause the component setup code generated by UI Designer\nfor such fields to be ignored." + }, + "defaultConfiguration": { + "enabled": false, + "level": "warning", + "parameters": { + "suppressToolId": "BoundFieldAssignment", + "ideaSeverity": "WARNING", + "qodanaSeverity": "High" + } + }, + "relationships": [ + { + "target": { + "id": "UI form", + "index": 85, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + } + ], + "language": "en-US", + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "isComprehensive": false + }, + { + "name": "org.intellij.qodana", + "version": "233.14714", + "rules": [ + { + "id": "KotlinAnnotator", + "shortDescription": { + "text": "Kotlin annotator" + }, + "fullDescription": { + "text": "Allows viewing the problems reported by the Kotlin annotator: compilation problems, references unresolved by the IDE, and so on.", + "markdown": "Allows viewing the problems reported by the Kotlin annotator: compilation problems, references unresolved by the IDE, and so on." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "KotlinAnnotator", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "General", + "index": 44, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + }, + { + "id": "QodanaSanity", + "shortDescription": { + "text": "Sanity" + }, + "fullDescription": { + "text": "Reports issues essential to this file like syntax errors, unresolved methods and variables, etc...", + "markdown": "Reports issues essential to this file like syntax errors, unresolved methods and variables, etc..." + }, + "defaultConfiguration": { + "enabled": false, + "level": "error", + "parameters": { + "suppressToolId": "QodanaSanity", + "ideaSeverity": "ERROR", + "qodanaSeverity": "Critical" + } + }, + "relationships": [ + { + "target": { + "id": "Qodana", + "index": 122, + "toolComponent": { + "name": "QDJVMC" + } + }, + "kinds": [ + "superset" + ] + } + ] + } + ], + "language": "en-US", + "contents": [ + "localizedData", + "nonLocalizedData" + ], + "isComprehensive": false + } + ] + }, + "invocations": [ + { + "startTimeUtc": "2024-04-08T07:32:06.682230631Z", + "exitCode": 0, + "executionSuccessful": true + } + ], + "language": "en-US", + "versionControlProvenance": [ + { + "repositoryUri": "https://github.com/verronpro/docx-stamper.git", + "revisionId": "daa191d3c91be576e6b80f36f1162aad567b3155", + "branch": "refs/heads/main", + "properties": { + "repoUrl": "https://github.com/verronpro/docx-stamper", + "lastAuthorName": "Caring Coder", + "vcsType": "Git", + "lastAuthorEmail": "joseph@verron.pro" + } + } + ], + "results": [ + { + "ruleId": "SuspiciousMethodCalls", + "kind": "fail", + "level": "warning", + "message": { + "text": "Suspicious call to 'Map.containsKey()'", + "markdown": "Suspicious call to 'Map.containsKey()'" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java", + "uriBaseId": "SRCROOT" + }, + "region": { + "startLine": 103, + "startColumn": 42, + "charOffset": 3938, + "charLength": 3, + "snippet": { + "text": "run" + }, + "sourceLanguage": "JAVA" + }, + "contextRegion": { + "startLine": 101, + "startColumn": 1, + "charOffset": 3820, + "charLength": 242, + "snippet": { + "text": " while (!q.isEmpty()) {\n ContentAccessor run = q.remove();\n if (replacements.containsKey(run)\n && run instanceof Child child\n && child.getParent() instanceof ContentAccessor parent) {" + }, + "sourceLanguage": "JAVA" + } + }, + "logicalLocations": [ + { + "fullyQualifiedName": "docx-stamper", + "kind": "module" + } + ] + } + ], + "partialFingerprints": { + "equalIndicator/v1": "0686bdb901ed8d95d380fd1ded399082e62b377662d1832f5567065f60515bd7" + }, + "properties": { + "ideaSeverity": "WARNING", + "qodanaSeverity": "High", + "tags": [ + "JAVA" + ] + } + }, + { + "ruleId": "SuspiciousMethodCalls", + "kind": "fail", + "level": "warning", + "message": { + "text": "Suspicious call to 'Map.get()'", + "markdown": "Suspicious call to 'Map.get()'" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "src/main/java/org/wickedsource/docxstamper/processor/repeat/RepeatDocPartProcessor.java", + "uriBaseId": "SRCROOT" + }, + "region": { + "startLine": 108, + "startColumn": 52, + "charOffset": 4242, + "charLength": 3, + "snippet": { + "text": "run" + }, + "sourceLanguage": "JAVA" + }, + "contextRegion": { + "startLine": 106, + "startColumn": 1, + "charOffset": 4063, + "charLength": 249, + "snippet": { + "text": " List parentContent = parent.getContent();\n parentContent.add(parentContent.indexOf(run),\n replacements.get(run));\n parentContent.remove(run);\n } else {" + }, + "sourceLanguage": "JAVA" + } + }, + "logicalLocations": [ + { + "fullyQualifiedName": "docx-stamper", + "kind": "module" + } + ] + } + ], + "partialFingerprints": { + "equalIndicator/v1": "5a9cfb8c5513b152dd7c772434cf7ca685b127b12df3feca9b6e41d9f6a8d7ac" + }, + "properties": { + "ideaSeverity": "WARNING", + "qodanaSeverity": "High", + "tags": [ + "JAVA" + ] + } + } + ], + "automationDetails": { + "id": "project/qodana/2024-04-08", + "guid": "2753617f-1077-45db-8100-74aa1871144a", + "properties": { + "jobUrl": "https://github.com/verronpro/docx-stamper/actions/runs/8596227495" + } + }, + "newlineSequences": [ + "\r\n", + "\n" + ], + "properties": { + "configProfile": "absent", + "deviceId": "200820300000000-cb28-b39f-8fe8-4f10a2b2f48e", + "qodanaNewResultSummary": { + "high": 2, + "total": 2 + } + } + } + ] +} \ No newline at end of file From a605a548329567a1e3dcfcbad62c609c19dfa8f8 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Mon, 13 May 2024 23:42:48 +0800 Subject: [PATCH 128/134] Refactor OfficeStampers to use StreamStamper In the OfficeStampers.java file, updated docxStamper methods to return StreamStamper instances instead of OfficeStamper instances. In addition, implemented a new method to handle WordprocessingMLPackage loading that catches and rethrows Docx4JException as OfficeStamperException. --- .../officestamper/preset/OfficeStampers.java | 59 ++++++++++++------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/src/main/java/pro/verron/officestamper/preset/OfficeStampers.java b/src/main/java/pro/verron/officestamper/preset/OfficeStampers.java index 4f524831..f3bbcc57 100644 --- a/src/main/java/pro/verron/officestamper/preset/OfficeStampers.java +++ b/src/main/java/pro/verron/officestamper/preset/OfficeStampers.java @@ -1,11 +1,15 @@ package pro.verron.officestamper.preset; +import org.docx4j.openpackaging.exceptions.Docx4JException; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.wickedsource.docxstamper.DocxStamper; import org.wickedsource.docxstamper.preprocessor.MergeSameStyleRuns; import org.wickedsource.docxstamper.preprocessor.RemoveProofErrors; -import pro.verron.officestamper.api.OfficeStamper; import pro.verron.officestamper.api.OfficeStamperConfiguration; +import pro.verron.officestamper.api.OfficeStamperException; +import pro.verron.officestamper.api.StreamStamper; + +import java.io.InputStream; /** * Main class of the docx-stamper library. @@ -19,27 +23,38 @@ */ public class OfficeStampers { - /** - * Creates a new instance of the {@link DocxStamper} class with the specified {@link OfficeStamperConfiguration}. - * - * @param config the configuration for the docx stamper - * - * @return a new instance of the {@link DocxStamper} class - */ - public static OfficeStamper docxStamper( - OfficeStamperConfiguration config - ) { - return new DocxStamper<>(config); - } + /** + * Creates a new DocxStamper with the default configuration. + * Also adds the {@link RemoveProofErrors} and {@link MergeSameStyleRuns} preprocessors. + * + * @return a new DocxStamper + */ + public static StreamStamper docxStamper() { + return docxStamper(OfficeStamperConfigurations.standardWithPreprocessing()); + } + + /** + * Creates a new instance of the {@link DocxStamper} class with the specified {@link OfficeStamperConfiguration}. + * + * @param config the configuration for the docx stamper + * + * @return a new instance of the {@link DocxStamper} class + */ + public static StreamStamper docxStamper( + OfficeStamperConfiguration config + ) { + return new StreamStamper<>( + OfficeStampers::loadWord, + new DocxStamper(config) + ); + } - /** - * Creates a new DocxStamper with the default configuration. - * Also adds the {@link RemoveProofErrors} and {@link MergeSameStyleRuns} preprocessors. - * - * @return a new DocxStamper - */ - public static OfficeStamper docxStamper() { - return new DocxStamper<>(OfficeStamperConfigurations.standardWithPreprocessing()); - } + private static WordprocessingMLPackage loadWord(InputStream is) { + try { + return WordprocessingMLPackage.load(is); + } catch (Docx4JException e) { + throw new OfficeStamperException(e); + } + } } From 41e6eb481283c9b97fa8d4244266b296c03f7b4f Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Mon, 13 May 2024 23:49:37 +0800 Subject: [PATCH 129/134] Update import in EvaluationContexts.java The import statement for EvaluationContextConfigurer in the EvaluationContexts.java file has been changed to use the version available in the pro.verron.officestamper package instead of the one from the org.wickedsource.docxstamper package. --- .../main/java/pro/verron/officestamper/EvaluationContexts.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/officestamper-examples/src/main/java/pro/verron/officestamper/EvaluationContexts.java b/officestamper-examples/src/main/java/pro/verron/officestamper/EvaluationContexts.java index 6572729f..438f26a5 100644 --- a/officestamper-examples/src/main/java/pro/verron/officestamper/EvaluationContexts.java +++ b/officestamper-examples/src/main/java/pro/verron/officestamper/EvaluationContexts.java @@ -1,7 +1,7 @@ package pro.verron.officestamper; import org.springframework.context.expression.MapAccessor; -import org.wickedsource.docxstamper.api.EvaluationContextConfigurer; +import pro.verron.officestamper.api.EvaluationContextConfigurer; public class EvaluationContexts { static EvaluationContextConfigurer enableMapAccess() { From b474b840db0435ac8e83d770f18c0652d80f623e Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Mon, 13 May 2024 23:50:04 +0800 Subject: [PATCH 130/134] Update diagnostic stamping method to use new standard A new standard configuration and stamper has been added to the diagnostic stamping procedure. Additionally, the old diagnostic stamping method has been renamed to legacyStampDiagnostic, and a new method, stampDiagnostic, has been introduced. This new method uses the Officestampers.docxStamper with the new standard configuration. The changes enable a more standardized way of stamping the diagnostic data. --- .../pro/verron/officestamper/Examples.java | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/officestamper-examples/src/main/java/pro/verron/officestamper/Examples.java b/officestamper-examples/src/main/java/pro/verron/officestamper/Examples.java index a6bf1078..69eed784 100644 --- a/officestamper-examples/src/main/java/pro/verron/officestamper/Examples.java +++ b/officestamper-examples/src/main/java/pro/verron/officestamper/Examples.java @@ -2,18 +2,20 @@ import org.wickedsource.docxstamper.DocxStamper; import org.wickedsource.docxstamper.DocxStamperConfiguration; +import pro.verron.officestamper.preset.OfficeStampers; import java.io.OutputStream; import java.util.Map; import java.util.logging.Logger; import static pro.verron.officestamper.EvaluationContexts.enableMapAccess; +import static pro.verron.officestamper.preset.OfficeStamperConfigurations.standard; public class Examples { public static final Logger logger = Utils.getLogger(); - public static void stampDiagnostic(OutputStream outputStream) { + public static void legacyStampDiagnostic(OutputStream outputStream) { logger.info("Start of the diagnostic stamping procedure"); logger.info("Setup a map-reading able docx-stamper instance"); @@ -45,4 +47,37 @@ public static void stampDiagnostic(OutputStream outputStream) { logger.info("End of the diagnostic stamping procedure"); } + public static void stampDiagnostic(OutputStream outputStream) { + logger.info("Start of the diagnostic stamping procedure"); + + logger.info("Setup a map-reading able docx-stamper instance"); + + var configuration = standard() + .setEvaluationContextConfigurer(enableMapAccess()); + var stamper = OfficeStampers.docxStamper(configuration); + + logger.info("Load the internally packaged 'Diagnostic.docx' template resource"); + var template = Utils.streamResource("Diagnostic.docx"); + + logger.info(""" + Create a context with: \ + system environment variables, \ + jvm properties, \ + and user preferences"""); + + var diagnosticMaker = new Diagnostic(); + var context = Map.of( + "reportDate", diagnosticMaker.date(), + "reportUser", diagnosticMaker.user(), + "environment", diagnosticMaker.environmentVariables(), + "properties", diagnosticMaker.jvmProperties(), + "preferences", diagnosticMaker.userPreferences() + ); + + logger.info("Start stamping process"); + stamper.stamp(template, context, outputStream); + + logger.info("End of the diagnostic stamping procedure"); + } + } From 69cddecc58527371463822be7a9bfdc27a53dfc4 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Mon, 13 May 2024 23:50:26 +0800 Subject: [PATCH 131/134] Update Main.java to support legacy and new stamping methods The Main.java file has been updated to support both the older (legacy) and the newer methods of "stamping" files. The two methods are demonstrated separately via the legacyStampDiagnostic() and stampDiagnostic() methods. A helper method, createOutStream(), has been added for creating output streams. --- .../java/pro/verron/officestamper/Main.java | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/officestamper-examples/src/main/java/pro/verron/officestamper/Main.java b/officestamper-examples/src/main/java/pro/verron/officestamper/Main.java index 06faaff2..bd219743 100644 --- a/officestamper-examples/src/main/java/pro/verron/officestamper/Main.java +++ b/officestamper-examples/src/main/java/pro/verron/officestamper/Main.java @@ -1,19 +1,33 @@ package pro.verron.officestamper; +import java.io.IOException; +import java.io.OutputStream; import java.nio.file.Files; import java.util.logging.Level; import java.util.logging.Logger; +import static pro.verron.officestamper.Examples.legacyStampDiagnostic; +import static pro.verron.officestamper.Examples.stampDiagnostic; + public class Main { private static final Logger logger = Utils.getLogger(); public static void main(String[] args) throws Exception { - var outputPath = Files.createTempFile("Diagnostic-", ".docx"); - var outputStream = Files.newOutputStream(outputPath); + // Use the way prior to docx-stamper 1.6.8 + legacyStampDiagnostic(createOutStream("LegacyDiagnostic-")); + + // Use the way from docx-stamper 1.6.8 and with office-stamper + stampDiagnostic(createOutStream("Diagnostic-")); + } + + private static OutputStream createOutStream(String prefix) + throws IOException { + var outputPath = Files.createTempFile(prefix, ".docx"); logger.log(Level.INFO, "Stamping to file: " + outputPath); - Examples.stampDiagnostic(outputStream); + return Files.newOutputStream(outputPath); } + } From ed0a316525b4289da9c87d0ab99295b02321ea1c Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Mon, 13 May 2024 23:50:41 +0800 Subject: [PATCH 132/134] Update version from SNAPSHOT to release in pom.xml files Changes made in this commit indicate version change for artifact 'docx-stamper'. The SNAPSHOT version 1.6.8-SNAPSHOT has been updated to release version 1.6.8 in both main and officestamper-examples pom.xml files. --- officestamper-examples/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/officestamper-examples/pom.xml b/officestamper-examples/pom.xml index b114c3c3..563f188e 100644 --- a/officestamper-examples/pom.xml +++ b/officestamper-examples/pom.xml @@ -18,7 +18,7 @@ pro.verron docx-stamper - 1.6.8-SNAPSHOT + 1.6.8 diff --git a/pom.xml b/pom.xml index d604220a..88b266bd 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 pro.verron docx-stamper - 1.6.8-SNAPSHOT + 1.6.8 jar docx-stamper From d4c4b44e1517aa1e6cc055133dae5bf9f7273d23 Mon Sep 17 00:00:00 2001 From: Joseph Verron Date: Mon, 13 May 2024 23:55:16 +0800 Subject: [PATCH 133/134] Simplify TestDocxStamper constructor The constructor of TestDocxStamper has been simplified by removing the Function instance, and directly calling the 'OfficeStampers.docxStamper' method. This reduces the code complexity and potential error sources linked to catching exceptions. --- .../verron/officestamper/test/TestDocxStamper.java | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/test/java/pro/verron/officestamper/test/TestDocxStamper.java b/src/test/java/pro/verron/officestamper/test/TestDocxStamper.java index b4796d2d..3bf6ac77 100644 --- a/src/test/java/pro/verron/officestamper/test/TestDocxStamper.java +++ b/src/test/java/pro/verron/officestamper/test/TestDocxStamper.java @@ -18,7 +18,6 @@ import java.io.OutputStream; import java.util.List; import java.util.Optional; -import java.util.function.Function; import java.util.stream.Stream; import static java.util.stream.Collectors.joining; @@ -42,15 +41,7 @@ public final class TestDocxStamper { * @since 1.6.6 */ public TestDocxStamper(OfficeStamperConfiguration config) { - Function loader = inputStream -> { - try { - return WordprocessingMLPackage.load(inputStream); - } catch (Docx4JException e) { - throw new RuntimeException(e); - } - }; - stamper = new StreamStamper<>(loader, - OfficeStampers.docxStamper(config)); + stamper = OfficeStampers.docxStamper(config); } /** From f70abd78b91fc041ba8febba79a5283d9c61817f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 16:00:14 +0000 Subject: [PATCH 134/134] Bump org.springframework:spring-expression from 6.1.4 to 6.1.6 Bumps [org.springframework:spring-expression](https://github.com/spring-projects/spring-framework) from 6.1.4 to 6.1.6. - [Release notes](https://github.com/spring-projects/spring-framework/releases) - [Commits](https://github.com/spring-projects/spring-framework/compare/v6.1.4...v6.1.6) --- updated-dependencies: - dependency-name: org.springframework:spring-expression dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 88b266bd..a9b72174 100644 --- a/pom.xml +++ b/pom.xml @@ -317,7 +317,7 @@ org.springframework spring-expression - 6.1.4 + 6.1.6 org.springframework