[seam-commits] Seam SVN: r8318 - trunk/ui/src/main/java/org/jboss/seam/ui/validator.

seam-commits at lists.jboss.org seam-commits at lists.jboss.org
Mon Jun 2 07:54:08 EDT 2008


Author: christian.bauer at jboss.com
Date: 2008-06-02 07:54:08 -0400 (Mon, 02 Jun 2008)
New Revision: 8318

Modified:
   trunk/ui/src/main/java/org/jboss/seam/ui/validator/FormattedTextValidator.java
Log:
JBSEAM-3061, better customizable FormattedTextValidator

Modified: trunk/ui/src/main/java/org/jboss/seam/ui/validator/FormattedTextValidator.java
===================================================================
--- trunk/ui/src/main/java/org/jboss/seam/ui/validator/FormattedTextValidator.java	2008-06-02 11:47:36 UTC (rev 8317)
+++ trunk/ui/src/main/java/org/jboss/seam/ui/validator/FormattedTextValidator.java	2008-06-02 11:54:08 UTC (rev 8318)
@@ -12,8 +12,7 @@
 import org.jboss.seam.text.SeamTextLexer;
 import org.jboss.seam.text.SeamTextParser;
 
-import antlr.RecognitionException;
-import antlr.TokenStreamException;
+import antlr.*;
 
 /**
  * Formatted Text validator
@@ -27,19 +26,24 @@
  * and call the static convenience method
  * <tt>FormattedTextValidator.getErrorMessage(originalText, recognitionException)</tt>
  * if you want to display or log a nice error message.
- * 
+ * </p>
+ * <p>
+ * Uses an instance of <tt>SeamTextParser</tt> by default, override if you require
+ * validation with your customized instance of <tt>SeamTextParser</tt>.
+ * </p>
+ *
  * @author matthew.drees
  * @author Christian Bauer
  */
-public class FormattedTextValidator implements javax.faces.validator.Validator,
-        Serializable {
+public class FormattedTextValidator implements javax.faces.validator.Validator, Serializable {
+
     private static final long serialVersionUID               = 1L;
-
     private static final int  NUMBER_OF_CONTEXT_CHARS_AFTER  = 10;
     private static final int  NUMBER_OF_CONTEXT_CHARS_BEFORE = 10;
+    private static final String END_OF_TEXT = "END OF TEXT";
+    String firstError;
+    String firstErrorDetail;
 
-    String                    firstError;
-
     /**
      * Validate the given value as well-formed Seam Text. If there are parse
      * errors, throw a ValidatorException including the first parse error.
@@ -47,6 +51,7 @@
     public void validate(FacesContext context, UIComponent component,
             Object value) throws ValidatorException {
         firstError = null;
+        firstErrorDetail = null;
         if (value == null) {
             return;
         }
@@ -56,9 +61,7 @@
                     + value);
         }
         String text = (String) value;
-        Reader r = new StringReader(text);
-        SeamTextLexer lexer = new SeamTextLexer(r);
-        SeamTextParser parser = new SeamTextParser(lexer);
+        SeamTextParser parser = getSeamTextParser(text);
         try {
             parser.startRule();
         }
@@ -68,40 +71,146 @@
             // Problem with the token input stream
             throw new RuntimeException(tse);
         } catch (RecognitionException re) {
-            // A parser error, just log and swallow
+            // A parser error
             if (firstError == null) {
-                firstError = getErrorMessage(text, re);
+                firstError = getParserErrorMessage(text, re);
+                firstErrorDetail = re.getMessage().replace("\uFFFF",END_OF_TEXT);
             }
         }
 
         if (firstError != null) {
-            throw new ValidatorException(new FacesMessage("Invalid markup: "
-                    + firstError));
+            throw new ValidatorException(new FacesMessage(firstError, firstErrorDetail));
         }
     }
 
     /**
+     * Override to instantiate a custom <tt>SeamTextLexer</tt> and <tt>SeamTextParser</tt>.
+     *
+     * @param text the raw markup text
+     * @return an instance of <tt>SeamTextParser</tt>
+     */
+    public SeamTextParser getSeamTextParser(String text) {
+       Reader r = new StringReader(text);
+       SeamTextLexer lexer = new SeamTextLexer(r);
+       return new SeamTextParser(lexer);
+    }
+
+    public String getParserErrorMessage(String originalText, RecognitionException re) {
+        String parserErrorMsg;
+        if (NoViableAltException.class.isAssignableFrom(re.getClass())) {
+            parserErrorMsg = getNoViableAltErrorMessage(
+                re.getMessage(),
+                getErrorLocation(originalText, re, getNumberOfCharsBeforeErrorLocation(), getNumberOfCharsAfterErrorLocation())
+            );
+        } else if (MismatchedTokenException.class.isAssignableFrom(re.getClass())) {
+            parserErrorMsg = getMismatchedTokenErrorMessage(
+                re.getMessage(),
+                getErrorLocation(originalText, re, getNumberOfCharsBeforeErrorLocation(), getNumberOfCharsAfterErrorLocation())
+            );
+        } else if (SemanticException.class.isAssignableFrom(re.getClass())) {
+            parserErrorMsg = getSemanticErrorMessage(re.getMessage());
+        } else {
+            parserErrorMsg = re.getMessage();
+        }
+        return parserErrorMsg;
+    }
+
+    public int getNumberOfCharsBeforeErrorLocation() {
+        return NUMBER_OF_CONTEXT_CHARS_BEFORE;
+    }
+
+    public int getNumberOfCharsAfterErrorLocation() {
+        return NUMBER_OF_CONTEXT_CHARS_AFTER;
+    }
+
+    /**
+     * Override (e.g. for i18n) ANTLR parser error messages.
+     *
+     * @param originalMessage the ANTLR parser error message of the RecognitionException
+     * @param location a snippet that indicates the location in the original markup, might be null
+     * @return a message that is thrown by this validator
+     */
+    public String getNoViableAltErrorMessage(String originalMessage, String location) {
+        return location != null
+                ? "Text parsing error at '..." + location.trim() + "...'."
+                : "Text parsing error, " + originalMessage.replace("\uFFFF",END_OF_TEXT);
+    }
+
+    /**
+     * Override (e.g. for i18n) ANTLR parser error messages.
+     *
+     * @param originalMessage the ANTLR parser error message of the RecognitionException
+     * @param location a snippet that indicates the location in the original markup, might be null
+     * @return a message that is thrown by this validator
+     */
+    public String getMismatchedTokenErrorMessage(String originalMessage, String location) {
+        return location != null
+                ? "Text parsing error at '..." + location.trim() + "...'."
+                : "Text parsing error, " + originalMessage.replace("\uFFFF",END_OF_TEXT);
+    }
+
+    /**
+     * Override (e.g. for i18n) ANTLR parser error messages.
+     *
+     * @param originalMessage the ANTLR parser error message of the RecognitionException
+     * @return a message that is thrown by this validator
+     */
+    public String getSemanticErrorMessage(String originalMessage) {
+        return "Text parsing error, " + originalMessage.replace("\uFFFF",END_OF_TEXT) + ".";
+    }
+
+    /**
      * Extracts the error from the <tt>RecognitionException</tt> and generates
-     * a message with some helpful context.
+     * a location of the error by extracting the original text at the exceptions
+     * line and column.
      * 
      * @param originalText
      *            the original Seam Text markup as fed into the parser
      * @param re
      *            an ANTLR <tt>RecognitionException</tt> thrown by the parser
+     * @param charsBefore
+     *            characters before error location included in message
+     * @param charsAfter
+     *            characters after error location included in message
      * @return an error message with some helpful context about where the error
      *         occured
      */
-    public static String getErrorMessage(String originalText,
-            RecognitionException re) {
+    public static String getErrorLocation(String originalText, RecognitionException re, int charsBefore, int charsAfter) {
 
+        int beginIndex = Math.max(re.getColumn() - 1 - charsBefore, 0);
+        int endIndex = Math.min(re.getColumn() + charsAfter, originalText.length());
+
+        String location = null;
+
         // Avoid IOOBE even if what we show is wrong, we need to figure out why the indexes are off sometimes
-        int beginIndex = Math.max(re.getColumn() - 1 - NUMBER_OF_CONTEXT_CHARS_BEFORE, 0);
-        int endIndex = Math.min(re.getColumn() + NUMBER_OF_CONTEXT_CHARS_AFTER, originalText.length());
-        String snippet = originalText.length() > 50 ? originalText.substring(0, 50) : originalText;
         if (beginIndex > 0 && beginIndex < endIndex && endIndex > 0 && endIndex < originalText.length())
-            snippet = "..." + originalText.substring(beginIndex, endIndex) + "...";
+            location = originalText.substring(beginIndex, endIndex);
 
-        String msg = re.getMessage() + " at '" + snippet + "'";
-        return msg.replace("\n", " ").replace("\r", " ").replace("\uFFFF","END OF TEXT").replace("#{", "# {");
+        if (location == null) return location;
+
+        // Filter some dangerous characters we do not want in error messages
+        return location.replace("\n", " ").replace("\r", " ").replace("#{", "# {");
     }
+
+    /**
+     * Extracts the error from the <tt>RecognitionException</tt> and generates
+     * a message including the location of the error.
+     *
+     * @param originalText
+     *            the original Seam Text markup as fed into the parser
+     * @param re
+     *            an ANTLR <tt>RecognitionException</tt> thrown by the parser
+     * @return an error message with some helpful context about where the error
+     *         occured
+     */
+    public static String getErrorMessage(String originalText, RecognitionException re) {
+        return re.getMessage().replace("\uFFFF",END_OF_TEXT)
+                + " at '"
+                + getErrorLocation(
+                    originalText, re,
+                    NUMBER_OF_CONTEXT_CHARS_BEFORE, NUMBER_OF_CONTEXT_CHARS_AFTER
+                  )
+                + "'";
+
+    }
 }




More information about the seam-commits mailing list