ph-css

Build Status  Join the chat at https://gitter.im/phax/ph-css

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 :)

Maven usage

Add the following to your pom.xml to use this artifact:

<dependency>
  <groupId>com.helger</groupId>
  <artifactId>ph-css</artifactId>
  <version>6.2.3</version>
</dependency>

To build ph-css from source, Maven 3.0.4 is required. Any Maven version below does NOT work!

Documentation

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.

Coding paradigms used

Please look at my personal Coding Styleguide for the naming conventions used in this project.

Basic Classes

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:

CSS reading

ph-css contains two different possibilities to read CSS data:

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.

Recoverable Errors

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):

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.

Unrecoverable Errors

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):

As 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.

CSS iteration/visiting

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

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 URL handling

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.

Shorthand property handling

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:

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:

Even though no color value was provided, the default value black is returned. For all "sub-declarations", sensible default values are defined.

CSS utilities

ph-css contains a multitude of small utility class covering different aspects of CSS

Code Examples

Known shortcomings

The following list gives an overview of known shortcomings in ph-css

ph-csscompress-maven-plugin

A Maven plugin to compress CSS files at build time using ph-css.

It requires Java 8 and Maven 3 to run.

Maven configuration

      <plugin>
        <groupId>com.helger.maven</groupId>
        <artifactId>ph-csscompress-maven-plugin</artifactId>
        <version>6.2.3</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:

News and noteworthy


My personal Coding Styleguide | On Twitter: @philiphelger | Kindly supported by YourKit Java Profiler