Advanced Java CSS 3 parser and builder
Java CSS 2 and CSS 3 parser and builder. This version supersedes phloc-css.
The Maven plugin to compress CSS files at build time is located in sub-project ph-csscompress-maven-plugin and described further down.
ph-css has no logic for applying CSS onto HTML elements. This page shows some basic code examples that can be used to use the library. All snippets are free for any use.
ph-css and ph-csscompress-maven-plugin are both licensed under the Apache 2.0 license.
ph-css is used as a part of Apache JMeter 3 đ
Add the following to your pom.xml to use this artifact, replacing x.y.z
with the latest version:
<dependency>
<groupId>com.helger</groupId>
<artifactId>ph-css</artifactId>
<version>x.y.z</version>
</dependency>
To build ph-css from source, Maven 3.0.4 is required. Any Maven version below does NOT work!
As ph-css is mainly concerned about the grammatical structure of CSS, the main classes are for reading and writing CSS. Additionally it offers the possibility to traverse the elements in a CSS and make modifications on them.
Please look at my personal Coding Styleguide for the naming conventions used in this project.
A complete stylesheet is represented as an instance of com.helger.css.decl.CascadingStyleSheet
. There is no difference between CSS 2.1 and CSS 3.0 instances. The class com.helger.css.decl.CascadingStyleSheet
contains all top-level rules that may be present in a CSS:
@import
) - com.helger.css.decl.CSSImportRule
@namespace
) - com.helger.css.decl.CSSNamespaceRule
div{color:red;}
) - com.helger.css.decl.CSSStyleRule
@page
) - com.helger.css.decl.CSSPageRule
@media
) - com.helger.css.decl.CSSMediaRule
@font-face
) - com.helger.css.decl.CSSFontFaceRule
@keyframes
) - com.helger.css.decl.CSSKeyframesRule
@viewport
) - com.helger.css.decl.CSSViewportRule
@supports
) - com.helger.css.decl.CSSSupportsRule
@foo
) - com.helger.css.decl.CSSUnknownRule
ph-css contains two different possibilities to read CSS data:
com.helger.css.reader.CSSReader
. The result in this case will be an instance of com.helger.css.decl.CascadingStyleSheet
.style
element) can be achieved using com.helger.css.reader.CSSReaderDeclarationList
. The result in this case will be an instance of com.helger.css.decl.CSSDeclarationList
.Both reading classes support the reading from either a java.io.File
, a java.io.Reader
, a com.helger.commons.io.IInputStreamProvider
or a String
. The reason why java.io.InputStream
is not supported directly is because internally the stream is passed twice - first to determine a potentially available charset and second to read the content with the correctly determined charset. Thatâs why an IInputStreamProvider
must be used, that creates 2 unique input streams!
Note: reading from a String
is possible in two different ways: one that requires a charset and one that doesnât. The version with the charset treats the String
as if it was created from a byte array and tries to determine the charset like any other byte array based version. The version without a charset assumes that the String
was already created with the correct charset, and any @charset
rule contained in the CSS is ignored.
Since v3.8.2 the class com.helger.css.reader.CSSReaderSettings
is present and encapsulates the CSS version, the fallback charset, the recoverable error handler (see below) and the unrecoverable error handler (also see below) in one settings object. This settings object can be used for multiple invocations of CSSReader
and CSSReaderDeclarationList
.
ph-css differentiates between recoverable errors and unrecoverable errors. An example for a recoverable error is e.g. an @import
rule in the wrong place or a missing closing bracket within a style declaration. For recoverable errors a special handler interface com.helger.css.reader.errorhandler.ICSSParseErrorHandler
is present. You can pass an implementation of this error handler to the CSS reader (see above). The following implementations are present by default (all residing in package com.helger.css.reader.errorhandler
):
DoNothingCSSParseErrorHandler
- silently ignoring all recoverable errorsLoggingCSSParseErrorHandler
- logging all recoverable errors to an SLF4J loggerThrowingCSSParseErrorHandler
- throws a com.helger.css.parser.ParseException
in case of a recoverable error which is afterwards handled by the unrecoverable error handler (see below). This can be used to enforce handling only 100% valid CSS files. This is the default setting, if no error handler is specified during reading.CollectingCSSParseErrorHandler
- collects all recoverable errors into a list of com.helger.css.reader.errorhandler.CSSParseError
instances for later evaluation.Some error handlers can be nested so that a combination of a logging handler and a collecting handler can easily be achieved like:
new CollectingCSSParseErrorHandler (new LoggingCSSParseErrorHandler ())
DoNothingCSSParseErrorHandler
and ThrowingCSSParseErrorHandler
cannot be nested because it makes no sense.
Both CSSReader
and CSSReaderDeclarationList
have the possibility to define a default recoverable error handler using the method setDefaultParseErrorHandler(ICSSParseErrorHandler)
. If a reading method is invoked without an explicit ICSSParseErrorHandler
than this default error handler is used.
In case of an unrecoverable error, the underlying parser engine of JavaCC throws a com.helger.css.parser.ParseException
. This exception contains all the necessary information on where the error occurred. In case of such an unrecoverable error, the result of the reading will always be null
and the exception is not automatically propagated to the caller. To explicitly get notified when such a parse error occurs, the handler interface com.helger.css.handler.ICSSParseExceptionCallback
is available. The available implementations are (all residing in package com.helger.css.handler
):
DoNothingCSSParseExceptionHandler
- silently ignore all unrecoverable errorsLoggingCSSParseExceptionHandler
- log all unrecoverable errors to an SLF4J loggerAs there is at most one unrecoverable error per parse there is no collecting implementation of an ICSSParseExceptionCallback
available. If it is desired to propagate the Exception to the caller you need to implement your own ICSSParseExceptionCallback
subclass that throws an unchecked exception (one derived from RuntimeException
). Example:
final ICSSParseExceptionCallback aThrowingExceptionHandler = new ICSSParseExceptionCallback () {
public void onException (final ParseException ex) {
throw new IllegalStateException ("Failed to parse CSS", ex);
}
};
Both CSSReader
and CSSReaderDeclarationList
have the possibility to define a default unrecoverable error handler using the method setDefaultParseExceptionHandler(ICSSParseExceptionCallback)
. If a reading method is invoked without an explicit ICSSParseExceptionCallback
than this default exception handler is used.
Once a CSS file was successfully read, it can easily be iterated using the class com.helger.css.decl.visit.CSSVisitor
. It requires a valid instance of com.helger.css.decl.CascadingStyleSheet
as well as an implementation of com.helger.css.decl.visit.ICSSVisitor
. The CascadingStyleSheet
can be acquired either by reading from a file/stream or by creating a new one from scratch. For the ICSSVisitor
it is recommended to use the class com.helger.css.decl.visit.DefaultCSSVisitor
as the base class - this class contains empty implementations of all methods defined in the ICSSVisitor
interface. To visit all declarations (e.g. color:red;
) it is sufficient to simply override the method public void onDeclaration (@Nonnull final CSSDeclaration aDeclaration)
. For details please have a look at the JavaDocs of ICSSVisitor
. To start the visiting call CSSVisitor.visitCSS (CascadingStyleSheet, ICSSVisitor)
.
A special visitor is present for URLs. URLs can occur on several places in CSS files, especially in the @import
rules and within declarations (like in background-image: url(../images/bg.gif)
). Therefore a special interface com.helger.css.decl.visit.ICSSUrlVisitor
together with the empty default implementation com.helger.css.decl.visit.DefaultCSSUrlVisitor
is provided. So to visit all URLs within a CSS call CSSVisitor.visitCSSUrl(CascadingStyleSheet, ICSSUrlVisitor)
.
Since v6.2.3 the URL visitor recursively descends into expression members (see issue #59).
For modifying URLs (e.g. to adopt paths to a different environment) a special base class com.helger.css.decl.visit.AbstractModifyingCSSUrlVisitor
is available. It offers the abstract method protected abstract String getModifiedURI (@Nonnull String sURI)
to modify a URL and write the result back into the original CascadingStyleSheet
. An example of how this can be used, can be found in the test method com.helger.css.decl.visit.CSSVisitorDeclarationListTest.testModifyingCSSUrlVisitor ()
.
Note: it is safe to modify a CSS while iterating it, but only changes affecting children of the current node may be considered during the same iteration run.
CSS writing is performed with the class com.helger.css.writer.CSSWriter
. The most basic settings can be passed either directly to the constructor or using an instance of com.helger.css.writer.CSSWriterSettings
which offers a quite find grained control of the output process. To write the content of a CascadingStyleSheet
or any ICSSWriteable
to an arbitrary java.io.Writer
, the method writeCSS
is what you need. If you want the CSS serialized to a String
the shortcut method getCSSAsString
is available. For the remaining configuration methods please check the JavaDoc.
By default all CSS code is pretty-printed. To create a minified version of the CSS code call setOptimizedOutput (true)
and setRemoveUnnecessaryCode (true)
on your CSSWriterSettings
object.
Data URLs are URLs that directly contain the content inline. A regular use case is referencing small images directly inside a CSS. During CSS parsing no special handling for data URLs is added. Instead they are stored in a String
like any other URL.
To special handle data URLs the class com.helger.css.utils.CSSDataURLHelper
offers the possibility to check if a URL is a data URL via public static boolean isDataURL (String)
. If this method returns true
the method public static CSSDataURL parseDataURL (String)
can be used to extract all the information contained in the data URL. This method returns null
if the passed URL is not a data URL.
A CSS shorthand property is a property that consists of multiple values. Classical examples are margin
or border
. ph-css contains support for selected shorthand properties. All shorthand related classes can be found in package com.helger.css.decl.shorthand
. The supported shorthand properties are:
background
font
border
border-top
border-right
border-bottom
border-left
border-width
border-style
border-color
margin
padding
outline
list-style
All of these shorthand properties are registered in class CSSShortHandRegistry
and you can manually register your own shorthand descriptors. The CSSShortHandRegistry
allows you to split a single CSSDeclaration
like border:1px dashed
into the corresponding âsub-declarationsâ:
// Parse a dummy declaration
final CSSDeclaration aDecl = CSSReaderDeclarationList.readFromString ("border:1px dashed", ECSSVersion.CSS30).getDeclarationAtIndex (0);
// Get the Shorthand descriptor for "border"
final CSSShortHandDescriptor aSHD = CSSShortHandRegistry.getShortHandDescriptor (ECSSProperty.BORDER);
// And now split it into pieces
final List <CSSDeclaration> aSplittedDecls = aSHD.getSplitIntoPieces (aDecl);
In the above example, aSplittedDecls
will contain 3 elements with the following content:
border-width:1px
border-style:dashed
border-color:black
Even though no color value was provided, the default value black
is returned. For all âsub-declarationsâ, sensible default values are defined.
ph-css contains a multitude of small utility class covering different aspects of CSS
com.helger.css.utils.CSSColorHelper
contains methods to read and write the different types of CSS color values (rgb, rgba, hsl, hsla and hex value)com.helger.css.utils.ECSSColor
contains the basic CSS colors as an enumerationcom.helger.css.ECSSUnit
contains all the default CSS units (like. px
or em
)com.helger.css.utils.CSSNumberHelper
contains methods for handling the combination of numeric values and units.com.helger.css.utils.CSSRectHelper
contains methods for handling CSS rect
values.com.helger.css.tools.MediaQueryTools
provides shortcut methods for wrapping a complete CascadingStyleSheet
in one or more media queries@font-face {
font-family: "Your typeface";
src: url("path/basename.eot");
src: local("local font name"),
url("path/basename.woff") format("woff"),
url("path/basename.otf") format("opentype"),
url("path/basename.svg#filename") format("svg");
}
sStyle
as CSS 3.0 and creates a CSSDeclarationList
from itcolor: red (not important)
background: fixed (important)
Import: foobar.css - source location reaches from [1/1] up to [1/21]
background - references: a.gif - source location reaches from [2/22] up to [2/31]
background-image - references: /my/folder/b.gif - source location reaches from [3/25] up to [3/47]
Import: /folder/foobar.css
background - references data URL with 158 bytes of content
background-image - references regular URL: /my/folder/b.gif
The following list gives an overview of known shortcomings in ph-css
A Maven plugin to compress CSS files at build time using ph-css.
It requires Java 8 and Maven 3 to run.
Replace x.y.z
with the version you want to use.
<plugin>
<groupId>com.helger.maven</groupId>
<artifactId>ph-csscompress-maven-plugin</artifactId>
<version>x.y.z</version>
<executions>
<execution>
<goals>
<goal>csscompress</goal>
</goals>
</execution>
</executions>
<configuration>
<forceCompress>false</forceCompress>
<removeUnnecessaryCode>true</removeUnnecessaryCode>
<quoteURLs>true</quoteURLs>
<verbose>true</verbose>
<sourceDirectory>${basedir}/src/main/resources</sourceDirectory>
</configuration>
</plugin>
Configuration items are:
File
sourceDirectory${basedir}/src/main/resources
boolean
recursivetrue
boolean
removeUnnecessaryCodefalse
boolean
quoteURLsfalse
boolean
writeNamespaceRules@namespace
rules be written?true
boolean
writeFontFaceRules@font-face
rules be written?true
boolean
writeKeyframesRules@keyframes
rules be written?true
boolean
writeMediaRules@media
rules be written?true
boolean
writePageRules@page
rules be written?true
boolean
writeViewportRules@viewport
rules be written?true
boolean
writeSupportsRules@supports
rules be written?true
boolean
writeUnknownRules@
rules be written?true
boolean
forceCompressfalse
boolean
verbosefalse
boolean
browserCompliantMode (since 1.4.0)false
.String
sourceEncodingUTF-8
String
targetFileExtension.min.css
String
targetEncoding (since 1.4.0)UTF-8
.:host
and ::slotted
. See issue #73 and PR #74 - thanks @shagkur:lang()
). See issue #72) - thanks @shagkurflex
shorthand propertyborder-width
. See PR #68 - thanks @rockwotj@media
rules. See issue #67 - thanks @yinkwok-ys@footnote
in @page
rules. See PR #64 - thanks @schmidti159CSSPropertyColors
calc
parsing (issue #57). The implications of this change are, that the special IE6 and IE7 hacks with $
and *
as identifier prefixes are no longer supported. If you need to parse CSS suitable for these old browsers, stay with v6.2.0.CSSWriterSettings.DEFAULT_SETTINGS
because of wrong initialization order (#53)-o-
media expression featuresnull
is returned in parsing (#41)ThrowingCSSParseErrorHandler
to LoggingCSSParseErrorHandler
for best browser compliant mode handlingCSSReaderSettings
(for performance reasons)calc
function (#48)calc
with spaces (as in width: calc( 100% - 2.4em );
)java.awt.Color
for improved Android compatibility.AbstractHasTopLevelRules
for type-safe access of top-level rules (#39)fr
, vmax
and Q
CSSValue
ICSSInterpretErrorHandler
(issue #33)ph-csscompress-maven-plugin
into this repositoryjava.awt.Color
for improved Android compatibility.ICSSInterpretErrorHandler
(issue #33)My personal Coding Styleguide |
It is appreciated if you star the GitHub project if you like it.