diff --git a/plugins/devkit/devkit-core/resources/messages/DevKitBundle.properties b/plugins/devkit/devkit-core/resources/messages/DevKitBundle.properties
index 00f2b2876b7e..2aad20067cc1 100644
--- a/plugins/devkit/devkit-core/resources/messages/DevKitBundle.properties
+++ b/plugins/devkit/devkit-core/resources/messages/DevKitBundle.properties
@@ -190,7 +190,8 @@ inspections.plugin.xml.do.not.use.template.text=Do not use template text ''{0}''
inspections.plugin.xml.must.not.contain.template.text=Must not contain template text ''{0}''
inspections.plugin.xml.value.exceeds.max.length=Value exceeds maximum length {0}
inspections.plugin.xml.value.must.have.minimum.length=Value must have minimum length of {0}
-inspections.plugin.xml.product.descriptor.invalid.date=Date must be in YYYYMMDD format
+inspections.plugin.xml.product.descriptor.invalid.date=Date must be a YYYYMMDD integer, or a placeholder (e.g. __DATE__)
+inspections.plugin.xml.product.descriptor.invalid.version=Version must be an integer, or a placeholder (e.g. __VERSION__)
inspections.plugin.xml.no.plugin.icon.svg.file=Plugin should provide ''{0}'' (Documentation)
inspections.plugin.xml.component.interface.class.redundant=Redundant tag 'interfaceClass'
inspections.plugin.xml.service.interface.class.redundant=Redundant attribute 'serviceInterface'
diff --git a/plugins/devkit/devkit-core/src/dom/ProductDescriptor.java b/plugins/devkit/devkit-core/src/dom/ProductDescriptor.java
index d539c935394b..c733440c3536 100644
--- a/plugins/devkit/devkit-core/src/dom/ProductDescriptor.java
+++ b/plugins/devkit/devkit-core/src/dom/ProductDescriptor.java
@@ -21,7 +21,7 @@ public interface ProductDescriptor extends DomElement {
@NotNull
@Required
- GenericAttributeValue getReleaseVersion();
+ GenericAttributeValue getReleaseVersion();
@Nullable
GenericAttributeValue getOptional();
diff --git a/plugins/devkit/devkit-core/src/inspections/PluginXmlDomInspection.java b/plugins/devkit/devkit-core/src/inspections/PluginXmlDomInspection.java
index 7f81271121cf..82e93814714d 100644
--- a/plugins/devkit/devkit-core/src/inspections/PluginXmlDomInspection.java
+++ b/plugins/devkit/devkit-core/src/inspections/PluginXmlDomInspection.java
@@ -805,17 +805,31 @@ public final class PluginXmlDomInspection extends DevKitPluginXmlInspectionBase
checkMaxLength(productDescriptor.getCode(), 15, holder);
String releaseDate = productDescriptor.getReleaseDate().getValue();
- if (releaseDate == null) return;
+ if (releaseDate != null && !isPlaceHolder(releaseDate)) {
+ try {
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd", Locale.US);
+ dateFormat.setLenient(false);
+ dateFormat.parse(releaseDate);
+ }
+ catch (ParseException e) {
+ holder.createProblem(productDescriptor.getReleaseDate(),
+ DevKitBundle.message("inspections.plugin.xml.product.descriptor.invalid.date"));
+ }
+ }
+ String version = productDescriptor.getReleaseVersion().getValue();
+ if (version != null && !isPlaceHolder(version)) {
+ try {
+ Integer.parseInt(version);
+ }
+ catch (NumberFormatException e) {
+ holder.createProblem(productDescriptor.getReleaseVersion(),
+ DevKitBundle.message("inspections.plugin.xml.product.descriptor.invalid.version"));
+ }
+ }
+ }
- try {
- SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd", Locale.US);
- dateFormat.setLenient(false);
- dateFormat.parse(releaseDate);
- }
- catch (ParseException e) {
- holder.createProblem(productDescriptor.getReleaseDate(),
- DevKitBundle.message("inspections.plugin.xml.product.descriptor.invalid.date"));
- }
+ private static boolean isPlaceHolder(@Nullable String value) {
+ return value != null && value.length() > 4 && value.startsWith("__") && value.endsWith("__");
}
private static void annotateAddToGroup(AddToGroup addToGroup, DomElementAnnotationHolder holder) {
diff --git a/plugins/devkit/devkit-java-tests/testData/codeInsight/productDescriptorInvalid.xml b/plugins/devkit/devkit-java-tests/testData/codeInsight/productDescriptorInvalid.xml
index e9c33962a251..e7accbda45cb 100644
--- a/plugins/devkit/devkit-java-tests/testData/codeInsight/productDescriptorInvalid.xml
+++ b/plugins/devkit/devkit-java-tests/testData/codeInsight/productDescriptorInvalid.xml
@@ -4,7 +4,7 @@
<version/>
0123456789012345"
- release-date="99991399"
- release-version=""
+ release-date="99991399"
+ release-version=""
optional="yes"/>
diff --git a/plugins/devkit/devkit-java-tests/testData/codeInsight/productDescriptorWithPlaceholders.xml b/plugins/devkit/devkit-java-tests/testData/codeInsight/productDescriptorWithPlaceholders.xml
new file mode 100644
index 000000000000..4dc5351dd384
--- /dev/null
+++ b/plugins/devkit/devkit-java-tests/testData/codeInsight/productDescriptorWithPlaceholders.xml
@@ -0,0 +1,11 @@
+
+ test
+ JetBrains
+ 1.0
+ some name
+
+
+
diff --git a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/codeInsight/PluginXmlFunctionalTest.groovy b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/codeInsight/PluginXmlFunctionalTest.groovy
index 8bce3e6f9bf2..f3ea41b750fe 100644
--- a/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/codeInsight/PluginXmlFunctionalTest.groovy
+++ b/plugins/devkit/devkit-java-tests/testSrc/org/jetbrains/idea/devkit/codeInsight/PluginXmlFunctionalTest.groovy
@@ -728,6 +728,10 @@ public class MyErrorHandler extends ErrorReportSubmitter {}
doHighlightingTest("productDescriptor.xml")
}
+ void testProductDescriptorWithPlaceholders() {
+ doHighlightingTest("productDescriptorWithPlaceholders.xml")
+ }
+
void testProductDescriptorInvalid() {
doHighlightingTest("productDescriptorInvalid.xml")
}