diff --git a/README.md b/README.md index af74e1f5..b5449dd4 100644 --- a/README.md +++ b/README.md @@ -15,17 +15,21 @@ ph-css and ph-csscompress-maven-plugin are both licensed under the **Apache 2.0 ## News and noteworthy * v5.0.2 - 2016-xx-yy - * Made tab size configurable (issue #29) - * Improved media expressions (issue #30) + * Made tab size configurable (issue #29) + * Improved media expressions (issue #30) + * Allowing to disable consistency checks in class `CSSValue` + * Made CSS interpretation warnings customizable with class `ICSSInterpretErrorHandler` (issue #33) * v5.0.1 - 2016-08-17 * Using "modern java template" for JavaCC parser - results in quicker execution * Enhancement issue #27 - * Intergated `ph-csscompress-maven-plugin` into this repository + * Integrated `ph-csscompress-maven-plugin` into this repository * Bug fix wrong OutputStream (issue #28) * v5.0.0 - 2016-06-12 * Using JDK8 as the basis * removed explicit grammar for CSS 2.1 (issue #20) * Added browser compliant error handler + * v4.1.6 - 2016-xx-yy + * Made CSS interpretation warnings customizable with class `ICSSInterpretErrorHandler` (issue #33) * v4.1.5 - 2016-09-16 * Improved media expressions (issue #30) * Integrated ph-csscompress-maven-plugin into this project (compatibility to 5.x) diff --git a/ph-css/src/main/java/com/helger/css/ECSSVersion.java b/ph-css/src/main/java/com/helger/css/ECSSVersion.java index bf57d4e8..067e74d8 100644 --- a/ph-css/src/main/java/com/helger/css/ECSSVersion.java +++ b/ph-css/src/main/java/com/helger/css/ECSSVersion.java @@ -30,8 +30,10 @@ public enum ECSSVersion implements IHasVersion { // Sort fields according to the version! - @DevelopersNote ("No CSS parser is available for 1.0!") CSS10(new Version (1, 0)), - @DevelopersNote ("Up to version 4.x of ph-css a special CSS 2.1 parser was available") CSS21(new Version (2, 1)), + @DevelopersNote ("No CSS parser is available for 1.0!") + CSS10(new Version (1, 0)), + @DevelopersNote ("Up to version 4.x of ph-css a special CSS 2.1 parser was available. Now it is the same as CSS 3.0") + CSS21(new Version (2, 1)), CSS30 (new Version (3, 0)); /** Latest version is CSS 3.0 */ diff --git a/ph-css/src/main/java/com/helger/css/handler/CSSHandler.java b/ph-css/src/main/java/com/helger/css/handler/CSSHandler.java index 6cad284b..eeefbc83 100644 --- a/ph-css/src/main/java/com/helger/css/handler/CSSHandler.java +++ b/ph-css/src/main/java/com/helger/css/handler/CSSHandler.java @@ -17,14 +17,19 @@ package com.helger.css.handler; import javax.annotation.Nonnull; +import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.Immutable; import com.helger.commons.ValueEnforcer; import com.helger.commons.annotation.PresentForCodeCoverage; +import com.helger.commons.concurrent.SimpleReadWriteLock; import com.helger.css.ECSSVersion; import com.helger.css.decl.CSSDeclarationList; import com.helger.css.decl.CascadingStyleSheet; import com.helger.css.parser.CSSNode; +import com.helger.css.reader.CSSReader; +import com.helger.css.reader.errorhandler.ICSSInterpretErrorHandler; +import com.helger.css.reader.errorhandler.LoggingCSSInterpretErrorHandler; /** * This class is the entry point for converting AST nodes from the parser to @@ -35,6 +40,11 @@ @Immutable public final class CSSHandler { + private static final SimpleReadWriteLock s_aRWLock = new SimpleReadWriteLock (); + + @GuardedBy ("s_aRWLock") + private static ICSSInterpretErrorHandler s_aDefaultInterpretErrorHandler = new LoggingCSSInterpretErrorHandler (); + @PresentForCodeCoverage private static final CSSHandler s_aInstance = new CSSHandler (); @@ -51,15 +61,37 @@ private CSSHandler () * @return Never null. */ @Nonnull + @Deprecated public static CascadingStyleSheet readCascadingStyleSheetFromNode (@Nonnull final ECSSVersion eVersion, @Nonnull final CSSNode aNode) + { + return readCascadingStyleSheetFromNode (eVersion, aNode, CSSReader.getDefaultInterpretErrorHandler ()); + } + + /** + * Create a {@link CascadingStyleSheet} object from a parsed object. + * + * @param eVersion + * The CSS version to use. May not be null. + * @param aNode + * The parsed CSS object to read. May not be null. + * @param aErrorHandler + * The error handler to be used. May not be null. + * @return Never null. + * @since 5.0.2 + */ + @Nonnull + public static CascadingStyleSheet readCascadingStyleSheetFromNode (@Nonnull final ECSSVersion eVersion, + @Nonnull final CSSNode aNode, + @Nonnull final ICSSInterpretErrorHandler aErrorHandler) { ValueEnforcer.notNull (eVersion, "Version"); ValueEnforcer.notNull (aNode, "Node"); if (!ECSSNodeType.ROOT.isNode (aNode, eVersion)) throw new CSSHandlingException (aNode, "Passed node is not a root node!"); + ValueEnforcer.notNull (aErrorHandler, "ErrorHandler"); - return new CSSNodeToDomainObject (eVersion).createCascadingStyleSheetFromNode (aNode); + return new CSSNodeToDomainObject (eVersion, aErrorHandler).createCascadingStyleSheetFromNode (aNode); } /** @@ -72,14 +104,36 @@ public static CascadingStyleSheet readCascadingStyleSheetFromNode (@Nonnull fina * @return Never null. */ @Nonnull + @Deprecated public static CSSDeclarationList readDeclarationListFromNode (@Nonnull final ECSSVersion eVersion, @Nonnull final CSSNode aNode) + { + return readDeclarationListFromNode (eVersion, aNode, CSSReader.getDefaultInterpretErrorHandler ()); + } + + /** + * Create a {@link CSSDeclarationList} object from a parsed object. + * + * @param eVersion + * The CSS version to use. May not be null. + * @param aNode + * The parsed CSS object to read. May not be null. + * @param aErrorHandler + * The error handler to be used. May not be null. + * @return Never null. + * @since 5.0.2 + */ + @Nonnull + public static CSSDeclarationList readDeclarationListFromNode (@Nonnull final ECSSVersion eVersion, + @Nonnull final CSSNode aNode, + @Nonnull final ICSSInterpretErrorHandler aErrorHandler) { ValueEnforcer.notNull (eVersion, "Version"); ValueEnforcer.notNull (aNode, "Node"); if (!ECSSNodeType.STYLEDECLARATIONLIST.isNode (aNode, eVersion)) throw new CSSHandlingException (aNode, "Passed node is not a style declaration node!"); + ValueEnforcer.notNull (aErrorHandler, "ErrorHandler"); - return new CSSNodeToDomainObject (eVersion).createDeclarationListFromNode (aNode); + return new CSSNodeToDomainObject (eVersion, aErrorHandler).createDeclarationListFromNode (aNode); } } diff --git a/ph-css/src/main/java/com/helger/css/handler/CSSNodeToDomainObject.java b/ph-css/src/main/java/com/helger/css/handler/CSSNodeToDomainObject.java index c2783589..fdd1dcf9 100644 --- a/ph-css/src/main/java/com/helger/css/handler/CSSNodeToDomainObject.java +++ b/ph-css/src/main/java/com/helger/css/handler/CSSNodeToDomainObject.java @@ -22,9 +22,6 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.NotThreadSafe; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.helger.commons.ValueEnforcer; import com.helger.commons.annotation.Nonempty; import com.helger.commons.collection.ext.CommonsArrayList; @@ -77,6 +74,7 @@ import com.helger.css.media.ECSSMedium; import com.helger.css.parser.CSSNode; import com.helger.css.parser.CSSParseHelper; +import com.helger.css.reader.errorhandler.ICSSInterpretErrorHandler; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @@ -89,19 +87,22 @@ @NotThreadSafe final class CSSNodeToDomainObject { - private static final Logger s_aLogger = LoggerFactory.getLogger (CSSNodeToDomainObject.class); - private final ECSSVersion m_eVersion; + private final ICSSInterpretErrorHandler m_aErrorHandler; /** * Constructor * * @param eVersion * The CSS version to use. May not be null. + * @param aErrorHandler + * Error handler to use. May not be null. */ - public CSSNodeToDomainObject (@Nonnull final ECSSVersion eVersion) + public CSSNodeToDomainObject (@Nonnull final ECSSVersion eVersion, + @Nonnull final ICSSInterpretErrorHandler aErrorHandler) { m_eVersion = ValueEnforcer.notNull (eVersion, "Version"); + m_aErrorHandler = ValueEnforcer.notNull (aErrorHandler, "ErrorHandler"); } private void _expectNodeType (@Nonnull final CSSNode aNode, @Nonnull final ECSSNodeType eExpected) @@ -115,11 +116,11 @@ private void _expectNodeType (@Nonnull final CSSNode aNode, @Nonnull final ECSSN "'"); } - private static void _throwUnexpectedChildrenCount (@Nonnull final CSSNode aNode, @Nonnull @Nonempty final String sMsg) + private void _throwUnexpectedChildrenCount (@Nonnull final CSSNode aNode, @Nonnull @Nonempty final String sMsg) { - s_aLogger.error (sMsg); + m_aErrorHandler.onCSSInterpretationError (sMsg); for (int i = 0; i < aNode.jjtGetNumChildren (); ++i) - s_aLogger.error (" " + aNode.jjtGetChild (i)); + m_aErrorHandler.onCSSInterpretationError (" " + aNode.jjtGetChild (i)); throw new CSSHandlingException (aNode, sMsg); } @@ -171,11 +172,12 @@ private CSSImportRule _createImportRule (@Nonnull final CSSNode aNode) ++nCurrentIndex; } else - s_aLogger.error ("Expected an MEDIALIST node but got " + ECSSNodeType.getNodeName (aMediaListNode, m_eVersion)); + m_aErrorHandler.onCSSInterpretationError ("Expected an MEDIALIST node but got " + + ECSSNodeType.getNodeName (aMediaListNode, m_eVersion)); } if (nCurrentIndex < nChildCount) - s_aLogger.error ("Import statement has children which are unhandled."); + m_aErrorHandler.onCSSInterpretationError ("Import statement has children which are unhandled."); return ret; } @@ -253,7 +255,7 @@ private ICSSSelectorMember _createSelectorMember (final CSSNode aNode) final String sText = aNode.getText (); final ECSSSelectorCombinator eCombinator = ECSSSelectorCombinator.getFromNameOrNull (sText); if (eCombinator == null) - s_aLogger.error ("Failed to parse CSS selector combinator '" + sText + "'"); + m_aErrorHandler.onCSSInterpretationError ("Failed to parse CSS selector combinator '" + sText + "'"); return eCombinator; } @@ -309,7 +311,8 @@ private ICSSSelectorMember _createSelectorMember (final CSSNode aNode) aNode.toString ()); } - s_aLogger.error ("Unsupported selector child: " + ECSSNodeType.getNodeName (aNode, m_eVersion)); + m_aErrorHandler.onCSSInterpretationError ("Unsupported selector child: " + + ECSSNodeType.getNodeName (aNode, m_eVersion)); return null; } @@ -375,15 +378,15 @@ private CSSExpressionMemberMathProduct _createExpressionMathProduct (@Nonnull fi final String sText = aChildChildNode.getText (); final ECSSMathOperator eMathOp = ECSSMathOperator.getFromNameOrNull (sText); if (eMathOp == null) - s_aLogger.error ("Failed to parse math operator '" + sText + "'"); + m_aErrorHandler.onCSSInterpretationError ("Failed to parse math operator '" + sText + "'"); else aNestedProduct.addMember (eMathOp); } else - s_aLogger.error ("Unsupported child of " + - ECSSNodeType.getNodeName (aChildNode, m_eVersion) + - ": " + - ECSSNodeType.getNodeName (aChildChildNode, m_eVersion)); + m_aErrorHandler.onCSSInterpretationError ("Unsupported child of " + + ECSSNodeType.getNodeName (aChildNode, m_eVersion) + + ": " + + ECSSNodeType.getNodeName (aChildChildNode, m_eVersion)); } ret.addMember (new CSSExpressionMemberMathUnitProduct (aNestedProduct)); } @@ -394,15 +397,15 @@ private CSSExpressionMemberMathProduct _createExpressionMathProduct (@Nonnull fi final String sText = aChildNode.getText (); final ECSSMathOperator eMathOp = ECSSMathOperator.getFromNameOrNull (sText); if (eMathOp == null) - s_aLogger.error ("Failed to parse math product operator '" + sText + "'"); + m_aErrorHandler.onCSSInterpretationError ("Failed to parse math product operator '" + sText + "'"); else ret.addMember (eMathOp); } else - s_aLogger.error ("Unsupported child of " + - ECSSNodeType.getNodeName (aNode, m_eVersion) + - ": " + - ECSSNodeType.getNodeName (aChildNode, m_eVersion)); + m_aErrorHandler.onCSSInterpretationError ("Unsupported child of " + + ECSSNodeType.getNodeName (aNode, m_eVersion) + + ": " + + ECSSNodeType.getNodeName (aChildNode, m_eVersion)); } return ret; @@ -470,15 +473,15 @@ private CSSExpressionMemberMath _createExpressionMathTerm (@Nonnull final CSSNod final String sText = aChildNode.getText (); final ECSSMathOperator eMathOp = ECSSMathOperator.getFromNameOrNull (sText); if (eMathOp == null) - s_aLogger.error ("Failed to parse math operator '" + sText + "'"); + m_aErrorHandler.onCSSInterpretationError ("Failed to parse math operator '" + sText + "'"); else ret.addMember (eMathOp); } else - s_aLogger.error ("Unsupported child of " + - ECSSNodeType.getNodeName (aNode, m_eVersion) + - ": " + - ECSSNodeType.getNodeName (aChildNode, m_eVersion)); + m_aErrorHandler.onCSSInterpretationError ("Unsupported child of " + + ECSSNodeType.getNodeName (aNode, m_eVersion) + + ": " + + ECSSNodeType.getNodeName (aChildNode, m_eVersion)); } return ret; @@ -540,16 +543,16 @@ private CSSExpression _createExpression (@Nonnull final CSSNode aNode) final String sText = aChildNode.getText (); final ECSSExpressionOperator eOp = ECSSExpressionOperator.getFromNameOrNull (sText); if (eOp == null) - s_aLogger.error ("Failed to parse expression operator '" + sText + "'"); + m_aErrorHandler.onCSSInterpretationError ("Failed to parse expression operator '" + sText + "'"); else ret.addMember (eOp); } else { - s_aLogger.error ("Unsupported child of " + - ECSSNodeType.getNodeName (aNode, m_eVersion) + - ": " + - ECSSNodeType.getNodeName (aChildNode, m_eVersion)); + m_aErrorHandler.onCSSInterpretationError ("Unsupported child of " + + ECSSNodeType.getNodeName (aNode, m_eVersion) + + ": " + + ECSSNodeType.getNodeName (aChildNode, m_eVersion)); } } return ret; @@ -585,10 +588,10 @@ private CSSDeclaration _createDeclaration (@Nonnull final CSSNode aNode) if (ECSSNodeType.IMPORTANT.isNode (aChildNode, m_eVersion)) bImportant = true; else - s_aLogger.error ("Expected an " + - ECSSNodeType.IMPORTANT.getNodeName (m_eVersion) + - " token but got a " + - ECSSNodeType.getNodeName (aChildNode, m_eVersion)); + m_aErrorHandler.onCSSInterpretationError ("Expected an " + + ECSSNodeType.IMPORTANT.getNodeName (m_eVersion) + + " token but got a " + + ECSSNodeType.getNodeName (aChildNode, m_eVersion)); } final CSSDeclaration ret = new CSSDeclaration (sProperty, aExpression, bImportant); @@ -628,7 +631,7 @@ private CSSStyleRule _createStyleRule (@Nonnull final CSSNode aNode) if (ECSSNodeType.SELECTOR.isNode (aChildNode, m_eVersion)) { if (!bSelectors) - s_aLogger.error ("Found a selector after a declaration!"); + m_aErrorHandler.onCSSInterpretationError ("Found a selector after a declaration!"); ret.addSelector (_createSelector (aChildNode)); } @@ -643,10 +646,10 @@ private CSSStyleRule _createStyleRule (@Nonnull final CSSNode aNode) } else if (!ECSSNodeType.isErrorNode (aChildNode, m_eVersion)) - s_aLogger.error ("Unsupported child of " + - ECSSNodeType.getNodeName (aNode, m_eVersion) + - ": " + - ECSSNodeType.getNodeName (aChildNode, m_eVersion)); + m_aErrorHandler.onCSSInterpretationError ("Unsupported child of " + + ECSSNodeType.getNodeName (aNode, m_eVersion) + + ": " + + ECSSNodeType.getNodeName (aChildNode, m_eVersion)); } } @@ -712,8 +715,8 @@ private CSSPageRule _createPageRule (@Nonnull final CSSNode aNode) } else if (!ECSSNodeType.isErrorNode (aBodyChildNode, m_eVersion)) - s_aLogger.error ("Unsupported page rule body child: " + - ECSSNodeType.getNodeName (aBodyChildNode, m_eVersion)); + m_aErrorHandler.onCSSInterpretationError ("Unsupported page rule body child: " + + ECSSNodeType.getNodeName (aBodyChildNode, m_eVersion)); } return ret; @@ -745,7 +748,8 @@ private CSSPageRule _createPageRule (@Nonnull final CSSNode aNode) } else if (!ECSSNodeType.isErrorNode (aChildNode, m_eVersion)) - s_aLogger.error ("Unsupported page rule child: " + ECSSNodeType.getNodeName (aChildNode, m_eVersion)); + m_aErrorHandler.onCSSInterpretationError ("Unsupported page rule child: " + + ECSSNodeType.getNodeName (aChildNode, m_eVersion)); } return ret; } @@ -794,8 +798,8 @@ private CSSMediaRule _createMediaRule (@Nonnull final CSSNode aNode) ret.addRule (_createSupportsRule (aChildNode)); else if (!ECSSNodeType.isErrorNode (aChildNode, m_eVersion)) - s_aLogger.error ("Unsupported media-rule child: " + - ECSSNodeType.getNodeName (aChildNode, m_eVersion)); + m_aErrorHandler.onCSSInterpretationError ("Unsupported media-rule child: " + + ECSSNodeType.getNodeName (aChildNode, m_eVersion)); } return ret; } @@ -809,7 +813,11 @@ private CSSMediaQuery _createMediaQuery (@Nonnull final CSSNode aNode) // CSS 2.1 compatibility final String sMedium = aNode.getText (); if (ECSSMedium.getFromNameOrNull (sMedium) == null) - s_aLogger.warn ("CSS " + m_eVersion.getVersionString () + " Media query uses unknown medium '" + sMedium + "'"); + m_aErrorHandler.onCSSInterpretationWarning ("CSS " + + m_eVersion.getVersionString () + + " Media query uses unknown medium '" + + sMedium + + "'"); final CSSMediaQuery ret = new CSSMediaQuery (EModifier.NONE, sMedium); ret.setSourceLocation (aNode.getSourceLocation ()); return ret; @@ -838,7 +846,7 @@ private CSSMediaQuery _createMediaQuery (@Nonnull final CSSNode aNode) if ("only".equalsIgnoreCase (sMediaModifier)) eModifier = EModifier.ONLY; else - s_aLogger.error ("Unsupported media modifier '" + sMediaModifier + "' found!"); + m_aErrorHandler.onCSSInterpretationError ("Unsupported media modifier '" + sMediaModifier + "' found!"); } ++nStartIndex; } @@ -853,11 +861,11 @@ private CSSMediaQuery _createMediaQuery (@Nonnull final CSSNode aNode) { sMedium = aNextChild.getText (); if (ECSSMedium.getFromNameOrNull (sMedium) == null) - s_aLogger.warn ("CSS " + - m_eVersion.getVersionString () + - " media query uses unknown medium '" + - sMedium + - "'"); + m_aErrorHandler.onCSSInterpretationWarning ("CSS " + + m_eVersion.getVersionString () + + " media query uses unknown medium '" + + sMedium + + "'"); ++nStartIndex; } } @@ -871,7 +879,8 @@ private CSSMediaQuery _createMediaQuery (@Nonnull final CSSNode aNode) ret.addMediaExpression (_createMediaExpr (aChildNode)); else if (!ECSSNodeType.isErrorNode (aChildNode, m_eVersion)) - s_aLogger.error ("Unsupported media query child: " + ECSSNodeType.getNodeName (aChildNode, m_eVersion)); + m_aErrorHandler.onCSSInterpretationError ("Unsupported media query child: " + + ECSSNodeType.getNodeName (aChildNode, m_eVersion)); } return ret; } @@ -890,7 +899,7 @@ private CSSMediaExpression _createMediaExpr (@Nonnull final CSSNode aNode) ECSSNodeType.getNodeName (aFeatureNode, m_eVersion)); final String sFeature = aFeatureNode.getText (); if (ECSSMediaExpressionFeature.getFromNameOrNull (sFeature) == null) - s_aLogger.warn ("Media expression uses unknown feature '" + sFeature + "'"); + m_aErrorHandler.onCSSInterpretationWarning ("Media expression uses unknown feature '" + sFeature + "'"); CSSMediaExpression ret; if (nChildCount == 1) @@ -925,7 +934,8 @@ private CSSFontFaceRule _createFontFaceRule (@Nonnull final CSSNode aNode) } else if (!ECSSNodeType.isErrorNode (aChildNode, m_eVersion)) - s_aLogger.error ("Unsupported font-face rule child: " + ECSSNodeType.getNodeName (aChildNode, m_eVersion)); + m_aErrorHandler.onCSSInterpretationError ("Unsupported font-face rule child: " + + ECSSNodeType.getNodeName (aChildNode, m_eVersion)); } return ret; } @@ -981,7 +991,8 @@ private CSSKeyframesRule _createKeyframesRule (@Nonnull final CSSNode aNode) } else if (!ECSSNodeType.isErrorNode (aChildNode, m_eVersion)) - s_aLogger.error ("Unsupported keyframes rule child: " + ECSSNodeType.getNodeName (aChildNode, m_eVersion)); + m_aErrorHandler.onCSSInterpretationError ("Unsupported keyframes rule child: " + + ECSSNodeType.getNodeName (aChildNode, m_eVersion)); ++nIndex; } @@ -1008,7 +1019,8 @@ private CSSViewportRule _createViewportRule (@Nonnull final CSSNode aNode) } else if (!ECSSNodeType.isErrorNode (aChildNode, m_eVersion)) - s_aLogger.error ("Unsupported viewport rule child: " + ECSSNodeType.getNodeName (aChildNode, m_eVersion)); + m_aErrorHandler.onCSSInterpretationError ("Unsupported viewport rule child: " + + ECSSNodeType.getNodeName (aChildNode, m_eVersion)); } return ret; } @@ -1095,13 +1107,14 @@ private ICSSSupportsConditionMember _createSupportsConditionMemberRecursive (@No return ret; } - s_aLogger.error ("Unsupported supportsConditionInParents child: " + - ECSSNodeType.getNodeName (aChildNode, m_eVersion)); + m_aErrorHandler.onCSSInterpretationError ("Unsupported supportsConditionInParents child: " + + ECSSNodeType.getNodeName (aChildNode, m_eVersion)); return null; } if (!ECSSNodeType.isErrorNode (aNode, m_eVersion)) - s_aLogger.error ("Unsupported supports-condition child: " + ECSSNodeType.getNodeName (aNode, m_eVersion)); + m_aErrorHandler.onCSSInterpretationError ("Unsupported supports-condition child: " + + ECSSNodeType.getNodeName (aNode, m_eVersion)); return null; } @@ -1150,8 +1163,8 @@ private CSSSupportsRule _createSupportsRule (@Nonnull final CSSNode aNode) ret.addRule (_createSupportsRule (aChildNode)); else if (!ECSSNodeType.isErrorNode (aChildNode, m_eVersion)) - s_aLogger.error ("Unsupported supports-rule child: " + - ECSSNodeType.getNodeName (aChildNode, m_eVersion)); + m_aErrorHandler.onCSSInterpretationError ("Unsupported supports-rule child: " + + ECSSNodeType.getNodeName (aChildNode, m_eVersion)); } return ret; } @@ -1233,10 +1246,11 @@ public CascadingStyleSheet createCascadingStyleSheetFromNode (@Nonnull final CSS ret.addRule (_createUnknownRule (aChildNode)); } else - s_aLogger.error ("Unsupported child of " + - ECSSNodeType.getNodeName (aNode, m_eVersion) + - ": " + - ECSSNodeType.getNodeName (aChildNode, m_eVersion)); + m_aErrorHandler.onCSSInterpretationError ("Unsupported child of " + + ECSSNodeType.getNodeName (aNode, m_eVersion) + + ": " + + ECSSNodeType.getNodeName (aChildNode, + m_eVersion)); } return ret; } diff --git a/ph-css/src/main/java/com/helger/css/parser/AbstractParserCSS.java b/ph-css/src/main/java/com/helger/css/parser/AbstractParserCSS.java index 2425dd7a..788796d2 100644 --- a/ph-css/src/main/java/com/helger/css/parser/AbstractParserCSS.java +++ b/ph-css/src/main/java/com/helger/css/parser/AbstractParserCSS.java @@ -37,35 +37,57 @@ public abstract class AbstractParserCSS protected ICSSParseErrorHandler m_aCustomErrorHandler; protected boolean m_bBrowserCompliantMode = false; + /** + * Set a custom error handler to use. + * + * @param aCustomErrorHandler + * The custom error handler to use. May be null. + */ public final void setCustomErrorHandler (@Nullable final ICSSParseErrorHandler aCustomErrorHandler) { m_aCustomErrorHandler = aCustomErrorHandler; } + /** + * @return The custom error handler to be used for this parser. May be + * null. + */ @Nullable public final ICSSParseErrorHandler getCustomErrorHandler () { return m_aCustomErrorHandler; } + /** + * Enable or disable browser compliant mode. + * + * @param bBrowserCompliantMode + * true to enable browser compliant mode, + * false to disable it. + */ public final void setBrowserCompliantMode (final boolean bBrowserCompliantMode) { m_bBrowserCompliantMode = bBrowserCompliantMode; } + /** + * @return true if browser compliant mode is active, + * false if not. By default browser compliant mode is + * disabled. + */ public final boolean isBrowserCompliantMode () { return m_bBrowserCompliantMode; } // Used when NODE_SCOPE_HOOK is true - for debugging only - // Package scope to avoid warning when NODE_SCOPE_HOOK is false public void jjtreeOpenNodeScope (final Node aNode) { if (s_aLogger.isDebugEnabled ()) s_aLogger.debug ("Opening scope for " + aNode.toString ()); } + // Used when NODE_SCOPE_HOOK is true - for debugging only public void jjtreeCloseNodeScope (final Node aNode) { if (s_aLogger.isDebugEnabled ()) diff --git a/ph-css/src/main/java/com/helger/css/propertyvalue/CSSValue.java b/ph-css/src/main/java/com/helger/css/propertyvalue/CSSValue.java index b63e02cb..1bf16e04 100644 --- a/ph-css/src/main/java/com/helger/css/propertyvalue/CSSValue.java +++ b/ph-css/src/main/java/com/helger/css/propertyvalue/CSSValue.java @@ -47,7 +47,35 @@ @NotThreadSafe public class CSSValue implements ICSSValue { + public static final boolean DEFAULT_CONSISTENCY_CHECKS_ENABLED = true; + private static final Logger s_aLogger = LoggerFactory.getLogger (CSSValue.class); + private static boolean s_bConsistencyChecksEnabled = DEFAULT_CONSISTENCY_CHECKS_ENABLED; + + /** + * @return true if consistency checks are enabled (by default), + * false if not. + * @since 5.0.2 + */ + public static boolean areConsistencyChecksEnabled () + { + return s_bConsistencyChecksEnabled; + } + + /** + * Enable or disable consistency checks. By default the consistency checks are + * enabled (for backwards compatibility) but if performance is a real matter, + * you may want to disable them globally. + * + * @param bEnabled + * true to enable them, false to disable + * them. + * @since 5.0.2 + */ + public static void setConsistencyChecksEnabled (final boolean bEnabled) + { + s_bConsistencyChecksEnabled = bEnabled; + } private ICSSProperty m_aProperty; private String m_sValue; @@ -142,18 +170,22 @@ public String getValue () public CSSValue setValue (@Nonnull final String sValue) { ValueEnforcer.notNull (sValue, "Value"); - if (!m_aProperty.isValidValue (sValue)) - s_aLogger.warn ("CSS: the value '" + - sValue + - "' is not valid for property '" + - m_aProperty.getPropertyName () + - "'"); - if (sValue.contains (CCSS.IMPORTANT_SUFFIX)) - s_aLogger.warn ("CSS: the value '" + - sValue + - "' should not contain the '" + - CCSS.IMPORTANT_SUFFIX + - "' string! Use 'setImportant' method instead."); + + if (areConsistencyChecksEnabled ()) + { + if (!m_aProperty.isValidValue (sValue)) + s_aLogger.warn ("CSS: the value '" + + sValue + + "' is not valid for property '" + + m_aProperty.getPropertyName () + + "'"); + if (sValue.contains (CCSS.IMPORTANT_SUFFIX)) + s_aLogger.warn ("CSS: the value '" + + sValue + + "' should not contain the '" + + CCSS.IMPORTANT_SUFFIX + + "' string! Use 'setImportant' method instead."); + } m_sValue = sValue.trim (); return this; } diff --git a/ph-css/src/main/java/com/helger/css/reader/CSSReader.java b/ph-css/src/main/java/com/helger/css/reader/CSSReader.java index 3511c048..0fe0820e 100644 --- a/ph-css/src/main/java/com/helger/css/reader/CSSReader.java +++ b/ph-css/src/main/java/com/helger/css/reader/CSSReader.java @@ -63,7 +63,9 @@ import com.helger.css.parser.ParserCSS30TokenManager; import com.helger.css.parser.ParserCSSCharsetDetector; import com.helger.css.parser.ParserCSSCharsetDetectorTokenManager; +import com.helger.css.reader.errorhandler.ICSSInterpretErrorHandler; import com.helger.css.reader.errorhandler.ICSSParseErrorHandler; +import com.helger.css.reader.errorhandler.LoggingCSSInterpretErrorHandler; import com.helger.css.reader.errorhandler.ThrowingCSSParseErrorHandler; /** @@ -88,6 +90,9 @@ public final class CSSReader @GuardedBy ("s_aRWLock") private static ICSSParseExceptionCallback s_aDefaultParseExceptionHandler = new LoggingCSSParseExceptionCallback (); + @GuardedBy ("s_aRWLock") + private static ICSSInterpretErrorHandler s_aDefaultInterpretErrorHandler = new LoggingCSSInterpretErrorHandler (); + @PresentForCodeCoverage private static final CSSReader s_aInstance = new CSSReader (); @@ -144,6 +149,32 @@ public static void setDefaultParseExceptionHandler (@Nonnull final ICSSParseExce s_aRWLock.writeLocked ( () -> s_aDefaultParseExceptionHandler = aDefaultParseExceptionHandler); } + /** + * @return The default interpret error handler to handle interpretation errors + * in successfully parsed CSS. Never null. + * @since 5.0.2 + */ + @Nonnull + public static ICSSInterpretErrorHandler getDefaultInterpretErrorHandler () + { + return s_aRWLock.readLocked ( () -> s_aDefaultInterpretErrorHandler); + } + + /** + * Set the default interpret error handler to handle interpretation errors in + * successfully parsed CSS. + * + * @param aDefaultErrorHandler + * The default error handler to be used. May not be null. + * @since 5.0.2 + */ + public static void setDefaultInterpretErrorHandler (@Nonnull final ICSSInterpretErrorHandler aDefaultErrorHandler) + { + ValueEnforcer.notNull (aDefaultErrorHandler, "DefaultErrorHandler"); + + s_aRWLock.writeLocked ( () -> s_aDefaultInterpretErrorHandler = aDefaultErrorHandler); + } + /** * Main reading of the CSS * @@ -1112,29 +1143,34 @@ public static CascadingStyleSheet readFromStream (@Nonnull final IHasInputStream aCharStream.setTabSize (aSettings.getTabSize ()); // Use the default CSS parse error handler if none is provided - ICSSParseErrorHandler aRealErrorHandler = aSettings.getCustomErrorHandler (); - if (aRealErrorHandler == null) - aRealErrorHandler = getDefaultParseErrorHandler (); + ICSSParseErrorHandler aRealParseErrorHandler = aSettings.getCustomErrorHandler (); + if (aRealParseErrorHandler == null) + aRealParseErrorHandler = getDefaultParseErrorHandler (); // Use the default CSS exception handler if none is provided - ICSSParseExceptionCallback aRealExceptionHandler = aSettings.getCustomExceptionHandler (); - if (aRealExceptionHandler == null) - aRealExceptionHandler = getDefaultParseExceptionHandler (); + ICSSParseExceptionCallback aRealParseExceptionHandler = aSettings.getCustomExceptionHandler (); + if (aRealParseExceptionHandler == null) + aRealParseExceptionHandler = getDefaultParseExceptionHandler (); final boolean bBrowserCompliantMode = aSettings.isBrowserCompliantMode (); final CSSNode aNode = _readStyleSheet (aCharStream, eVersion, - aRealErrorHandler, - aRealExceptionHandler, + aRealParseErrorHandler, + aRealParseExceptionHandler, bBrowserCompliantMode); - // Failed to interpret content as CSS? + // Failed to parse content as CSS? if (aNode == null) return null; + // Get the interpret error handler + ICSSInterpretErrorHandler aRealInterpretErrorHandler = aSettings.getInterpretErrorHandler (); + if (aRealInterpretErrorHandler == null) + aRealInterpretErrorHandler = getDefaultInterpretErrorHandler (); + // Convert the AST to a domain object - return CSSHandler.readCascadingStyleSheetFromNode (eVersion, aNode); + return CSSHandler.readCascadingStyleSheetFromNode (eVersion, aNode, aRealInterpretErrorHandler); } finally { @@ -1214,29 +1250,34 @@ public static CascadingStyleSheet readFromReader (@Nonnull final IHasReader aRP, aCharStream.setTabSize (aSettings.getTabSize ()); // Use the default CSS parse error handler if none is provided - ICSSParseErrorHandler aRealErrorHandler = aSettings.getCustomErrorHandler (); - if (aRealErrorHandler == null) - aRealErrorHandler = getDefaultParseErrorHandler (); + ICSSParseErrorHandler aRealParseErrorHandler = aSettings.getCustomErrorHandler (); + if (aRealParseErrorHandler == null) + aRealParseErrorHandler = getDefaultParseErrorHandler (); // Use the default CSS exception handler if none is provided - ICSSParseExceptionCallback aRealExceptionHandler = aSettings.getCustomExceptionHandler (); - if (aRealExceptionHandler == null) - aRealExceptionHandler = getDefaultParseExceptionHandler (); + ICSSParseExceptionCallback aRealParseExceptionHandler = aSettings.getCustomExceptionHandler (); + if (aRealParseExceptionHandler == null) + aRealParseExceptionHandler = getDefaultParseExceptionHandler (); final boolean bBrowserCompliantMode = aSettings.isBrowserCompliantMode (); final CSSNode aNode = _readStyleSheet (aCharStream, eVersion, - aRealErrorHandler, - aRealExceptionHandler, + aRealParseErrorHandler, + aRealParseExceptionHandler, bBrowserCompliantMode); - // Failed to interpret content as CSS? + // Failed to parse content as CSS? if (aNode == null) return null; + // Get the interpret error handler + ICSSInterpretErrorHandler aRealInterpretErrorHandler = aSettings.getInterpretErrorHandler (); + if (aRealInterpretErrorHandler == null) + aRealInterpretErrorHandler = getDefaultInterpretErrorHandler (); + // Convert the AST to a domain object - return CSSHandler.readCascadingStyleSheetFromNode (eVersion, aNode); + return CSSHandler.readCascadingStyleSheetFromNode (eVersion, aNode, aRealInterpretErrorHandler); } finally { diff --git a/ph-css/src/main/java/com/helger/css/reader/CSSReaderDeclarationList.java b/ph-css/src/main/java/com/helger/css/reader/CSSReaderDeclarationList.java index 3b358ee5..55b4ed51 100644 --- a/ph-css/src/main/java/com/helger/css/reader/CSSReaderDeclarationList.java +++ b/ph-css/src/main/java/com/helger/css/reader/CSSReaderDeclarationList.java @@ -50,7 +50,9 @@ import com.helger.css.parser.ParseException; import com.helger.css.parser.ParserCSS30; import com.helger.css.parser.ParserCSS30TokenManager; +import com.helger.css.reader.errorhandler.ICSSInterpretErrorHandler; import com.helger.css.reader.errorhandler.ICSSParseErrorHandler; +import com.helger.css.reader.errorhandler.LoggingCSSInterpretErrorHandler; import com.helger.css.reader.errorhandler.ThrowingCSSParseErrorHandler; /** @@ -74,6 +76,9 @@ public final class CSSReaderDeclarationList @GuardedBy ("s_aRWLock") private static ICSSParseExceptionCallback s_aDefaultParseExceptionHandler = new LoggingCSSParseExceptionCallback (); + @GuardedBy ("s_aRWLock") + private static ICSSInterpretErrorHandler s_aDefaultInterpretErrorHandler = new LoggingCSSInterpretErrorHandler (); + @PresentForCodeCoverage private static final CSSReaderDeclarationList s_aInstance = new CSSReaderDeclarationList (); @@ -102,9 +107,7 @@ public static ICSSParseErrorHandler getDefaultParseErrorHandler () */ public static void setDefaultParseErrorHandler (@Nullable final ICSSParseErrorHandler aDefaultParseErrorHandler) { - s_aRWLock.writeLocked ( () -> { - s_aDefaultParseErrorHandler = aDefaultParseErrorHandler; - }); + s_aRWLock.writeLocked ( () -> s_aDefaultParseErrorHandler = aDefaultParseErrorHandler); } /** @@ -131,9 +134,33 @@ public static void setDefaultParseExceptionHandler (@Nonnull final ICSSParseExce { ValueEnforcer.notNull (aDefaultParseExceptionHandler, "DefaultParseExceptionHandler"); - s_aRWLock.writeLocked ( () -> { - s_aDefaultParseExceptionHandler = aDefaultParseExceptionHandler; - }); + s_aRWLock.writeLocked ( () -> s_aDefaultParseExceptionHandler = aDefaultParseExceptionHandler); + } + + /** + * @return The default interpret error handler to handle interpretation errors + * in successfully parsed CSS. Never null. + * @since 5.0.2 + */ + @Nonnull + public static ICSSInterpretErrorHandler getDefaultInterpretErrorHandler () + { + return s_aRWLock.readLocked ( () -> s_aDefaultInterpretErrorHandler); + } + + /** + * Set the default interpret error handler to handle interpretation errors in + * successfully parsed CSS. + * + * @param aDefaultErrorHandler + * The default error handler to be used. May not be null. + * @since 5.0.2 + */ + public static void setDefaultInterpretErrorHandler (@Nonnull final ICSSInterpretErrorHandler aDefaultErrorHandler) + { + ValueEnforcer.notNull (aDefaultErrorHandler, "DefaultErrorHandler"); + + s_aRWLock.writeLocked ( () -> s_aDefaultInterpretErrorHandler = aDefaultErrorHandler); } /** @@ -739,23 +766,31 @@ public static CSSDeclarationList readFromReader (@Nonnull @WillClose final Reade final CSSCharStream aCharStream = new CSSCharStream (aReader); // Use the default CSS parse error handler if none is provided - ICSSParseErrorHandler aRealErrorHandler = aSettings.getCustomErrorHandler (); - if (aRealErrorHandler == null) - aRealErrorHandler = getDefaultParseErrorHandler (); + ICSSParseErrorHandler aRealParseErrorHandler = aSettings.getCustomErrorHandler (); + if (aRealParseErrorHandler == null) + aRealParseErrorHandler = getDefaultParseErrorHandler (); // Use the default CSS exception handler if none is provided - ICSSParseExceptionCallback aRealExceptionHandler = aSettings.getCustomExceptionHandler (); - if (aRealExceptionHandler == null) - aRealExceptionHandler = getDefaultParseExceptionHandler (); + ICSSParseExceptionCallback aRealParseExceptionHandler = aSettings.getCustomExceptionHandler (); + if (aRealParseExceptionHandler == null) + aRealParseExceptionHandler = getDefaultParseExceptionHandler (); - final CSSNode aNode = _readStyleDeclaration (aCharStream, eVersion, aRealErrorHandler, aRealExceptionHandler); + final CSSNode aNode = _readStyleDeclaration (aCharStream, + eVersion, + aRealParseErrorHandler, + aRealParseExceptionHandler); - // Failed to interpret content as CSS? + // Failed to parse content as CSS? if (aNode == null) return null; + // Get the interpret error handler + ICSSInterpretErrorHandler aRealInterpretErrorHandler = aSettings.getInterpretErrorHandler (); + if (aRealInterpretErrorHandler == null) + aRealInterpretErrorHandler = getDefaultInterpretErrorHandler (); + // Convert the AST to a domain object - return CSSHandler.readDeclarationListFromNode (eVersion, aNode); + return CSSHandler.readDeclarationListFromNode (eVersion, aNode, aRealInterpretErrorHandler); } finally { diff --git a/ph-css/src/main/java/com/helger/css/reader/CSSReaderSettings.java b/ph-css/src/main/java/com/helger/css/reader/CSSReaderSettings.java index 1e5e571b..c3a5f5cc 100644 --- a/ph-css/src/main/java/com/helger/css/reader/CSSReaderSettings.java +++ b/ph-css/src/main/java/com/helger/css/reader/CSSReaderSettings.java @@ -29,6 +29,7 @@ import com.helger.commons.string.ToStringGenerator; import com.helger.css.ECSSVersion; import com.helger.css.handler.ICSSParseExceptionCallback; +import com.helger.css.reader.errorhandler.ICSSInterpretErrorHandler; import com.helger.css.reader.errorhandler.ICSSParseErrorHandler; /** @@ -50,6 +51,7 @@ public class CSSReaderSettings implements ICloneable private ICSSParseExceptionCallback m_aCustomExceptionHandler; private boolean m_bBrowserCompliantMode = DEFAULT_BROWSER_COMPLIANT_MODE; private int m_nTabSize = DEFAULT_TAB_SIZE; + private ICSSInterpretErrorHandler m_aInterpretErrorHandler; public CSSReaderSettings () {} @@ -63,6 +65,7 @@ public CSSReaderSettings (@Nonnull final CSSReaderSettings aOther) m_aCustomExceptionHandler = aOther.m_aCustomExceptionHandler; m_bBrowserCompliantMode = aOther.m_bBrowserCompliantMode; m_nTabSize = aOther.m_nTabSize; + m_aInterpretErrorHandler = aOther.m_aInterpretErrorHandler; } /** @@ -226,6 +229,35 @@ public CSSReaderSettings setTabSize (@Nonnegative final int nTabSize) return this; } + /** + * @return The special error handler to be used to interpret a successfully + * parsed CSS. May be null. If this is null + * the default error handler from {@link CSSReader} is used. + * @since 5.0.2 + */ + @Nullable + public ICSSInterpretErrorHandler getInterpretErrorHandler () + { + return m_aInterpretErrorHandler; + } + + /** + * Set a special interpret error handler for handling errors in successfully + * parsed CSS. + * + * @param aInterpretErrorHandler + * The special error handler to be used. May be null to + * indicate to use the default error handler from {@link CSSReader}. + * @return this for chaining + * @since 5.0.2 + */ + @Nonnull + public CSSReaderSettings setInterpretErrorHandler (@Nullable final ICSSInterpretErrorHandler aInterpretErrorHandler) + { + m_aInterpretErrorHandler = aInterpretErrorHandler; + return this; + } + @Nonnull public CSSReaderSettings getClone () { @@ -241,6 +273,7 @@ public String toString () .append ("CustomExceptionHandler", m_aCustomExceptionHandler) .append ("BrowserCompliantMode", m_bBrowserCompliantMode) .append ("TabSize", m_nTabSize) + .append ("InterpretErrorHandler", m_aInterpretErrorHandler) .toString (); } } diff --git a/ph-css/src/main/java/com/helger/css/reader/errorhandler/DoNothingCSSInterpretErrorHandler.java b/ph-css/src/main/java/com/helger/css/reader/errorhandler/DoNothingCSSInterpretErrorHandler.java new file mode 100644 index 00000000..57ecdd7e --- /dev/null +++ b/ph-css/src/main/java/com/helger/css/reader/errorhandler/DoNothingCSSInterpretErrorHandler.java @@ -0,0 +1,50 @@ +/** + * Copyright (C) 2014-2016 Philip Helger (www.helger.com) + * philip[at]helger[dot]com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.helger.css.reader.errorhandler; + +import javax.annotation.Nonnull; + +import com.helger.commons.annotation.Nonempty; +import com.helger.commons.string.ToStringGenerator; + +/** + * An implementation of {@link ICSSInterpretErrorHandler} that does nothing. + * + * @author Philip Helger + * @since 5.0.2 + */ +public class DoNothingCSSInterpretErrorHandler implements ICSSInterpretErrorHandler +{ + public DoNothingCSSInterpretErrorHandler () + {} + + public void onCSSInterpretationWarning (@Nonnull @Nonempty final String sMessage) + { + /* really do nothing :) */ + } + + public void onCSSInterpretationError (@Nonnull @Nonempty final String sMessage) + { + /* really do nothing :) */ + } + + @Override + public String toString () + { + return new ToStringGenerator (this).toString (); + } +} diff --git a/ph-css/src/main/java/com/helger/css/reader/errorhandler/DoNothingCSSParseErrorHandler.java b/ph-css/src/main/java/com/helger/css/reader/errorhandler/DoNothingCSSParseErrorHandler.java index 4176d76a..9242bb34 100644 --- a/ph-css/src/main/java/com/helger/css/reader/errorhandler/DoNothingCSSParseErrorHandler.java +++ b/ph-css/src/main/java/com/helger/css/reader/errorhandler/DoNothingCSSParseErrorHandler.java @@ -38,21 +38,21 @@ public DoNothingCSSParseErrorHandler () public void onCSSParseError (@Nonnull final ParseException aParseEx, @Nullable final Token aLastSkippedToken) throws ParseException { - /* empty */ + /* really do nothing :) */ } public void onCSSUnexpectedRule (@Nonnull final Token aCurrentToken, @Nonnull @Nonempty final String sRule, @Nonnull @Nonempty final String sMsg) throws ParseException { - /* empty */ + /* really do nothing :) */ } public void onCSSBrowserCompliantSkip (@Nullable final ParseException ex, @Nonnull final Token aFromToken, @Nonnull final Token aToToken) throws ParseException { - /* empty */ + /* really do nothing :) */ } @Override diff --git a/ph-css/src/main/java/com/helger/css/reader/errorhandler/ICSSInterpretErrorHandler.java b/ph-css/src/main/java/com/helger/css/reader/errorhandler/ICSSInterpretErrorHandler.java new file mode 100644 index 00000000..6efc6496 --- /dev/null +++ b/ph-css/src/main/java/com/helger/css/reader/errorhandler/ICSSInterpretErrorHandler.java @@ -0,0 +1,47 @@ +/** + * Copyright (C) 2014-2016 Philip Helger (www.helger.com) + * philip[at]helger[dot]com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.helger.css.reader.errorhandler; + +import javax.annotation.Nonnull; + +import com.helger.commons.annotation.Nonempty; + +/** + * Special CSS error handler that is invoked during interpretation of a parsed + * CSS. + * + * @author Philip Helger + * @since 5.0.2 + */ +public interface ICSSInterpretErrorHandler +{ + /** + * Called when an interpretation error or warning occurs. + * + * @param sMessage + * The message text of the error. + */ + void onCSSInterpretationWarning (@Nonnull @Nonempty final String sMessage); + + /** + * Called when an interpretation error occurs. + * + * @param sMessage + * The message text of the error. + */ + void onCSSInterpretationError (@Nonnull @Nonempty final String sMessage); +} diff --git a/ph-css/src/main/java/com/helger/css/reader/errorhandler/LoggingCSSInterpretErrorHandler.java b/ph-css/src/main/java/com/helger/css/reader/errorhandler/LoggingCSSInterpretErrorHandler.java new file mode 100644 index 00000000..e75c56f3 --- /dev/null +++ b/ph-css/src/main/java/com/helger/css/reader/errorhandler/LoggingCSSInterpretErrorHandler.java @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2014-2016 Philip Helger (www.helger.com) + * philip[at]helger[dot]com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.helger.css.reader.errorhandler; + +import javax.annotation.Nonnull; +import javax.annotation.concurrent.Immutable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.helger.commons.annotation.Nonempty; +import com.helger.commons.string.ToStringGenerator; + +/** + * A logging implementation of {@link ICSSInterpretErrorHandler}. So in case a + * warning or an error occurs, the details are logged to an SLF4J logger. + * + * @author Philip Helger + * @since 5.0.2 + */ +@Immutable +public class LoggingCSSInterpretErrorHandler implements ICSSInterpretErrorHandler +{ + private static final Logger s_aLogger = LoggerFactory.getLogger (LoggingCSSInterpretErrorHandler.class); + + /** + * Default constructor. + */ + public LoggingCSSInterpretErrorHandler () + {} + + public void onCSSInterpretationWarning (@Nonnull @Nonempty final String sMessage) + { + s_aLogger.warn (sMessage); + } + + public void onCSSInterpretationError (@Nonnull @Nonempty final String sMessage) + { + s_aLogger.error (sMessage); + } + + @Override + public String toString () + { + return new ToStringGenerator (this).toString (); + } +} diff --git a/ph-css/src/test/java/com/helger/css/parser/ParserCSS30Test.java b/ph-css/src/test/java/com/helger/css/parser/ParserCSS30Test.java index 93034f4e..f21f13b3 100644 --- a/ph-css/src/test/java/com/helger/css/parser/ParserCSS30Test.java +++ b/ph-css/src/test/java/com/helger/css/parser/ParserCSS30Test.java @@ -27,6 +27,7 @@ import com.helger.css.decl.CascadingStyleSheet; import com.helger.css.decl.ICSSTopLevelRule; import com.helger.css.handler.CSSHandler; +import com.helger.css.reader.CSSReader; /** * Test class for class {@link ParserCSS30}. @@ -56,7 +57,9 @@ public void test2 () throws ParseException final CSSNode aNode = aParser.styleSheet (); assertNotNull (aNode); - final CascadingStyleSheet aCSS = CSSHandler.readCascadingStyleSheetFromNode (ECSSVersion.CSS30, aNode); + final CascadingStyleSheet aCSS = CSSHandler.readCascadingStyleSheetFromNode (ECSSVersion.CSS30, + aNode, + CSSReader.getDefaultInterpretErrorHandler ()); assertNotNull (aCSS); for (final ICSSTopLevelRule aTopLevelRule : aCSS.getAllFontFaceRules ()) diff --git a/ph-css/src/test/java/com/helger/css/supplementary/issues/Issue33Test.java b/ph-css/src/test/java/com/helger/css/supplementary/issues/Issue33Test.java index 07fdf197..d32c8ea9 100644 --- a/ph-css/src/test/java/com/helger/css/supplementary/issues/Issue33Test.java +++ b/ph-css/src/test/java/com/helger/css/supplementary/issues/Issue33Test.java @@ -16,30 +16,40 @@ */ package com.helger.css.supplementary.issues; +import static org.junit.Assert.assertEquals; + import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.helger.css.ECSSVersion; import com.helger.css.decl.CascadingStyleSheet; import com.helger.css.reader.CSSReader; import com.helger.css.reader.CSSReaderSettings; +import com.helger.css.reader.errorhandler.DoNothingCSSInterpretErrorHandler; import com.helger.css.writer.CSSWriter; import com.helger.css.writer.CSSWriterSettings; /** - * Test for issue 26: https://github.com/phax/ph-css/issues/33 + * Test for issue 33: https://github.com/phax/ph-css/issues/33 * * @author Philip Helger */ public final class Issue33Test { + private static final Logger s_aLogger = LoggerFactory.getLogger (Issue33Test.class); + @Test public void testIssue () { + // No log message may be issued in this test! + final String css = "@media \\0screen\\,screen\\9 {.test {margin-left: 0px}}"; final CSSReaderSettings aSettings = new CSSReaderSettings ().setCSSVersion (ECSSVersion.LATEST) - .setBrowserCompliantMode (true); + .setBrowserCompliantMode (true) + .setInterpretErrorHandler (new DoNothingCSSInterpretErrorHandler ()); final CascadingStyleSheet cascadingStyleSheet = CSSReader.readFromStringStream (css, aSettings); final CSSWriter writer = new CSSWriter (new CSSWriterSettings (ECSSVersion.LATEST, true)); - System.out.println (writer.getCSSAsString (cascadingStyleSheet)); + assertEquals ("@media \\0screen\\,screen\\9 {.test{margin-left:0}}", writer.getCSSAsString (cascadingStyleSheet)); } }