diff --git a/runtime/Java/src/org/antlr/v4/runtime/RuntimeMetaData.java b/runtime/Java/src/org/antlr/v4/runtime/RuntimeMetaData.java index 2d1c2123b..8d6a1e6c1 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/RuntimeMetaData.java +++ b/runtime/Java/src/org/antlr/v4/runtime/RuntimeMetaData.java @@ -56,13 +56,29 @@ import java.util.concurrent.CopyOnWriteArraySet; * By default, the {@link DefaultListener#INSTANCE} listener is automatically * registered. As long as the default listener is registered, it will always be * the last listener notified in the event of a version mismatch. This behavior - * ensures the exception it throws will not prevent {@link #checkVersion} from - * notifying custom listeners registered by a user. This default listener may be - * removed by calling {@link #removeListener} for + * ensures that custom listeners registered by a user will be notified even in + * the event the default listener throws an exception. This default listener may + * be removed by calling {@link #removeListener} for * {@link DefaultListener#INSTANCE} or {@link #clearListeners}. If required, it * may be re-registered by calling {@link #addListener}.
*/ public class RuntimeMetaData { + /** + * A compile-time constant containing the current version of the ANTLR 4 + * runtime library. + * + *+ * This compile-time constant value allows generated parsers and other + * libraries to include a literal reference to the version of the ANTLR 4 + * runtime library the code was compiled against.
+ * + *+ * During development (between releases), this value contains the + * expected next release version. For official releases, the value + * will be the actual published version of the library.
+ */ + public static final String VERSION = "4.3"; + /** * This class provides detailed information about a mismatch between the * version of the tool a parser was generated with, the version of the @@ -128,9 +144,9 @@ public class RuntimeMetaData { * block. *+ * For example, version strings x.y and x.y.z are considered "compatible", + * and this listener will not throw an exception. Likewise, version strings + * x.y-SNAPSHOT and x.y.z are considered "compatible" because the major and + * minor components x.y are the same in each.
+ * + *+ * For the purposes of this listener, version numbers are assumed to have + * the form + * major.minor.patch.revision-suffix, + * with the individual components defined as follows.
+ * + *+ * The default implementation only throws an exception when the reported + * version mismatch contains a mismatched major or + * minor version component. For details about the syntax of the + * input {@code version}, see the documentation for + * {@link DefaultListener}.
*/ @Override public void reportVersionMismatch(@NotNull VersionMismatchException ex) throws VersionMismatchException { - throw ex; + if (!isMinorVersionMatch(ex)) { + throw ex; + } + } + + /** + * Determines if the reported version mismatch are a match when + * considering only the major and minor version + * components of the version strings. + * + * @param ex a {@link VersionMismatchException} instance containing + * detailed information about the specific version mismatch detected + * @return {@code true} if the major and minor version + * components of the version strings match; otherwise, {@code false}. + */ + protected boolean isMinorVersionMatch(@NotNull VersionMismatchException ex) { + String generatingToolVersion = ex.generatingToolVersion; + if (generatingToolVersion != null) { + if (!getMajorMinorVersion(VERSION).equals(getMajorMinorVersion(generatingToolVersion))) { + return false; + } + } + + return getMajorMinorVersion(VERSION).equals(getMajorMinorVersion(ex.compileTimeRuntimeVersion)); + } + + /** + * Gets the major and minor version numbers from a version string. For + * details about the syntax of the input {@code version}, see the + * documentation for {@link DefaultListener}. + * + * @param version The complete version string. + * @return A string of the form major.minor containing + * only the major and minor components of the version string. + */ + @NotNull + protected String getMajorMinorVersion(@NotNull String version) { + int firstDot = version.indexOf('.'); + int secondDot = firstDot >= 0 ? version.indexOf('.', firstDot + 1) : -1; + int firstDash = version.indexOf('-'); + int referenceLength = version.length(); + if (secondDot >= 0) { + referenceLength = Math.min(referenceLength, secondDot); + } + + if (firstDash >= 0) { + referenceLength = Math.min(referenceLength, firstDash); + } + + return version.substring(0, referenceLength); } } @@ -210,22 +311,6 @@ public class RuntimeMetaData { listeners.clear(); } - /** - * A compile-time constant containing the current version of the ANTLR 4 - * runtime library. - * - *- * This compile-time constant value allows generated parsers and other - * libraries to include a literal reference to the version of the ANTLR 4 - * runtime library the code was compiled against.
- * - *- * During development (between releases), this value contains the - * expected next release version. For official releases, the value - * will be the actual published version of the library.
- */ - public static final String VERSION = "4.3"; - /** * Gets the currently executing version of the ANTLR 4 runtime library. * @@ -270,7 +355,8 @@ public class RuntimeMetaData { * This method does not perform any detection or filtering of semantic * changes between tool and runtime versions. It simply checks for a simple * version match and notifies the registered listeners any time a difference - * is detected. + * is detected. A default instance of {@link DefaultListener} is notified + * unless it is explicitly removed. * ** Note that some breaking changes between releases could result in other diff --git a/tool/src/org/antlr/v4/Tool.java b/tool/src/org/antlr/v4/Tool.java index 7df218901..3ec4e3395 100644 --- a/tool/src/org/antlr/v4/Tool.java +++ b/tool/src/org/antlr/v4/Tool.java @@ -48,6 +48,7 @@ import org.antlr.v4.parse.GrammarTreeVisitor; import org.antlr.v4.parse.ToolANTLRLexer; import org.antlr.v4.parse.ToolANTLRParser; import org.antlr.v4.parse.v3TreeGrammarException; +import org.antlr.v4.runtime.RuntimeMetaData; import org.antlr.v4.runtime.misc.LogManager; import org.antlr.v4.runtime.misc.Nullable; import org.antlr.v4.semantics.SemanticPipeline; @@ -87,7 +88,12 @@ import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; public class Tool { - public static final String VERSION = "4.3"; + public static final String VERSION; + static { + // Assigned in a static{} block to prevent the field from becoming a + // compile-time constant + VERSION = RuntimeMetaData.VERSION; + } public static final String GRAMMAR_EXTENSION = ".g4"; public static final String LEGACY_GRAMMAR_EXTENSION = ".g";