org.nuxeo.theme.html.CSSUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.nuxeo.theme.html.CSSUtils.java

Source

/*
 * (C) Copyright 2006-2007 Nuxeo SAS <http://nuxeo.com> and others
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Jean-Marc Orliaguet, Chalmers
 *
 * $Id$
 */

package org.nuxeo.theme.html;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.platform.web.common.vh.VirtualHostHelper;
import org.nuxeo.theme.formats.styles.Style;
import org.nuxeo.theme.presets.PresetManager;
import org.nuxeo.theme.presets.PresetType;
import org.nuxeo.theme.properties.OrderedProperties;
import org.nuxeo.theme.resources.ImageInfo;
import org.nuxeo.theme.resources.PresetInfo;
import org.nuxeo.theme.resources.ResourceBank;
import org.nuxeo.theme.themes.ThemeDescriptor;
import org.nuxeo.theme.themes.ThemeException;
import org.nuxeo.theme.themes.ThemeManager;

public final class CSSUtils {

    static final Log log = LogFactory.getLog(CSSUtils.class);

    private static final String EMPTY_CSS_SELECTOR = "EMPTY";

    private static final String CLASS_ATTR_PREFIX = "nxStyle";

    private static final String CSS_PROPERTIES_RESOURCE = "/nxthemes/html/styles/css.properties";

    private static final Pattern firstTagPattern = Pattern.compile("<(.*?)>", Pattern.DOTALL);

    private static final Pattern otherTagsPattern = Pattern.compile("<.*?>(.*)", Pattern.DOTALL);

    private static final Pattern classAttrPattern = Pattern.compile(" class=\"(.*?)\"", Pattern.DOTALL);

    private static final Pattern emptyCssSelectorPattern = Pattern.compile("(.*?)\\{(.*?)\\}", Pattern.DOTALL);

    private static final Pattern hexColorPattern = Pattern.compile(".*?#(\\p{XDigit}{3,6}).*?", Pattern.DOTALL);

    private static final Pattern rgbColorPattern = Pattern.compile(".*?rgb\\s*\\(\\s*([0-9,\\s]+)\\s*\\).*?",
            Pattern.DOTALL);

    private static final Pattern urlPattern = Pattern.compile("^url\\s*\\([\\s,\",\']*(.*?)[\\s,\",\']*\\)$",
            Pattern.DOTALL);

    private static final Pattern partialUrlPattern = Pattern
            .compile("url\\s*\\([\\s,\",\']*([^/].*?)[\\s,\",\']*\\)", Pattern.DOTALL);

    private static final Pattern rgbDigitPattern = Pattern.compile("([0-9]{1,3},[0-9]{1,3},[0-9]{1,3})");

    private static final Properties cssProperties = new OrderedProperties();

    static {
        org.nuxeo.theme.Utils.loadProperties(cssProperties, CSS_PROPERTIES_RESOURCE);
    }

    private CSSUtils() {
        // This class is not supposed to be instantiated.
    }

    public static Properties getCssProperties() {
        return cssProperties;
    }

    public static String styleToCss(final Style style, final Collection<String> viewNames,
            final boolean ignoreViewName, final boolean ignoreClassName, final boolean indent) {

        final StringBuilder sb = new StringBuilder();
        final StringBuilder pSb = new StringBuilder();
        for (String viewName : viewNames) {
            final String className = computeCssClassName(style);
            pSb.setLength(0);
            boolean addSpace = false;
            if (!ignoreClassName) {
                pSb.append('.').append(className);
                addSpace = true;
            }
            if (!ignoreViewName && !"*".equals(viewName)) {
                pSb.append(toUpperCamelCase(viewName));
                addSpace = true;
            }

            for (String path : style.getPathsForView(viewName)) {
                final Properties styleProperties = style.getPropertiesFor(viewName, path);
                // if (styleProperties.isEmpty()) {
                // continue;
                // }

                final String[] splitPaths = path.split(",");
                final int len = splitPaths.length;
                for (int i = 0; i < len; i++) {
                    sb.append(pSb);
                    if (addSpace && !"".equals(path)) {
                        sb.append(' ');
                    }
                    sb.append(splitPaths[i].trim());
                    if (i < len - 1) {
                        sb.append(", ");
                    }
                }
                sb.append(" {");
                if (indent) {
                    sb.append('\n');
                }

                for (String propertyName : styleProperties.stringPropertyNames()) {
                    String value = styleProperties.getProperty(propertyName);
                    if (value == null) {
                        continue;
                    }
                    if (indent) {
                        sb.append("  ");
                    }
                    sb.append(propertyName);
                    sb.append(':');
                    if (indent) {
                        sb.append(' ');
                    }
                    sb.append(value).append(';');
                    if (indent) {
                        sb.append('\n');
                    }
                }
                sb.append("}\n");
                if (indent) {
                    sb.append('\n');
                }

            }
        }
        return sb.toString();
    }

    public static String insertCssClass(final String markup, final String className) {
        final Matcher firstMatcher = firstTagPattern.matcher(markup);
        final Matcher othersMatcher = otherTagsPattern.matcher(markup);

        if (!(firstMatcher.find() && othersMatcher.find())) {
            return markup;
        }

        // find a 'class="...."' match
        String inBrackets = firstMatcher.group(1);
        final Matcher classAttrMatcher = classAttrPattern.matcher(inBrackets);

        // build a new 'class="..."' string
        final StringBuilder classAttributes = new StringBuilder();
        if (classAttrMatcher.find()) {
            classAttributes.append(classAttrMatcher.group(1));
            if (!classAttributes.toString().endsWith(" ")) {
                classAttributes.append(' ');
            }
        }

        // add new attributes
        classAttributes.append(className);

        if (classAttributes.length() == 0) {
            return markup;

        }
        // remove the old 'class="..."' attributes, if there were some
        inBrackets = inBrackets.replaceAll(classAttrPattern.toString(), "");

        // write the final markup
        if (inBrackets.endsWith("/")) {
            return String.format("<%s class=\"%s\" />%s", inBrackets.replaceAll("/$", "").trim(),
                    classAttributes.toString(), othersMatcher.group(1));
        }
        return String.format("<%s class=\"%s\">%s", inBrackets, classAttributes.toString(), othersMatcher.group(1));

    }

    public static String computeCssClassName(final Style style) {
        String collectionName = style.getCollection();
        String prefix = CLASS_ATTR_PREFIX;
        if (collectionName != null) {
            prefix = toCamelCase(collectionName);
        }
        return String.format("%s%s", prefix, style.getUid());
    }

    public static String replaceColor(String text, String before, String after) {
        Matcher m = hexColorPattern.matcher(text);
        text = text.trim();
        while (m.find()) {
            String found = "#" + optimizeHexColor(m.group(1));
            if (found.equals(before)) {
                text = text.replace(String.format("#%s", m.group(1)), after);
            }
        }
        m = rgbColorPattern.matcher(text);
        while (m.find()) {
            String found = "#" + optimizeHexColor(rgbToHex(m.group(1)));
            if (found.equals(before)) {
                text = text.replace(String.format("rgb(%s)", m.group(1)), after);
            }
        }
        return text;
    }

    public static String replaceImage(String text, String before, String after) {
        text = text.trim();
        Matcher m = urlPattern.matcher(text);
        if (m.matches()) {
            String found = String.format("url(%s)", m.group(1));
            if (found.equals(before)) {
                text = text.replace(String.format("url(%s)", m.group(1)), after);
            }
        }
        return text;
    }

    public static String optimizeHexColor(String value) {
        value = value.toLowerCase();
        if (value.length() != 6) {
            return value;
        }
        if ((value.charAt(0) == value.charAt(1)) && (value.charAt(2) == value.charAt(3))
                && (value.charAt(4) == value.charAt(5))) {
            return String.format("%s%s%s", value.charAt(0), value.charAt(2), value.charAt(4));
        }
        return value;
    }

    public static String rgbToHex(String value) {
        value = value.replaceAll("\\s", "");
        final Matcher m = rgbDigitPattern.matcher(value);
        final StringBuffer sb = new StringBuffer();
        while (m.find()) {
            final String[] rgb = m.group(1).split(",");
            final StringBuffer hexcolor = new StringBuffer();
            for (String element : rgb) {
                final int val = Integer.parseInt(element);
                if (val < 16) {
                    hexcolor.append("0");
                }
                hexcolor.append(Integer.toHexString(val));
            }
            m.appendReplacement(sb, hexcolor.toString());
        }
        m.appendTail(sb);
        return sb.toString();
    }

    public static List<String> extractCssColors(String value) {
        final List<String> colors = new ArrayList<String>();
        value = value.trim();
        Matcher m = hexColorPattern.matcher(value);
        while (m.find()) {
            colors.add("#" + optimizeHexColor(m.group(1)));
        }
        m = rgbColorPattern.matcher(value);
        while (m.find()) {
            colors.add("#" + optimizeHexColor(rgbToHex(m.group(1))));
        }
        return colors;
    }

    public static List<String> extractCssImages(String value) {
        final List<String> images = new ArrayList<String>();
        value = value.trim();
        Matcher m = urlPattern.matcher(value);
        if (m.matches()) {
            images.add(String.format("url(%s)", m.group(1)));
        }
        return images;
    }

    public static String toCamelCase(final String value) {
        if (value == null || value.trim().equals("")) {
            return value;
        }
        final String newValue = value.replaceAll("[^\\p{Alnum}]+", " ");
        final StringBuilder sb = new StringBuilder();
        final String[] parts = newValue.trim().split("\\s+");
        sb.append(parts[0].toLowerCase(Locale.ENGLISH));
        for (int i = 1; i < parts.length; ++i) {
            sb.append(parts[i].substring(0, 1).toUpperCase());
            sb.append(parts[i].substring(1).toLowerCase(Locale.ENGLISH));
        }
        return sb.toString();
    }

    public static String toUpperCamelCase(final String value) {
        if ("".equals(value)) {
            return "";
        }
        final String newValue = toCamelCase(value);
        final StringBuilder sb = new StringBuilder();
        sb.append(newValue.substring(0, 1).toUpperCase());
        sb.append(newValue.substring(1));
        return sb.toString();
    }

    public static String compressSource(final String source) throws ThemeException {
        String compressedSource = source;
        Reader in = null;
        Writer out = null;
        final CssCompressor compressor;
        final int linebreakpos = -1;
        try {
            in = new StringReader(source);
            out = new StringWriter();
            compressor = new CssCompressor(in);
            compressor.compress(out, linebreakpos);
            compressedSource = out.toString();

        } catch (IOException e) {
            throw new ThemeException("Could not compress CSS", e);
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    log.error(e, e);
                } finally {
                    out = null;
                }
            }
        }
        if (in != null) {
            try {
                in.close();
            } catch (IOException e) {
                log.error(e, e);
            } finally {
                in = null;
            }
        }
        return compressedSource;
    }

    public static String expandPartialUrls(String text, String cssContextPath) {
        Matcher m = partialUrlPattern.matcher(text);
        if (!cssContextPath.endsWith("/")) {
            cssContextPath += "/";
        }
        String replacement = String.format("url(%s$1)", Matcher.quoteReplacement(cssContextPath));
        return m.replaceAll(replacement);
    }

    public static String expandVariables(String text, String basePath, String collectionName,
            ThemeDescriptor themeDescriptor) {

        String themeName = themeDescriptor.getName();

        if (basePath != null) {
            text = text.replaceAll("\\$\\{basePath\\}", Matcher.quoteReplacement(basePath));
            text = text.replaceAll("\\$\\{org.nuxeo.ecm.contextPath\\}", Matcher.quoteReplacement(basePath));
        }

        String contextPath = VirtualHostHelper.getContextPathProperty();

        // Replace global presets
        for (PresetType preset : PresetManager.getGlobalPresets(null, null)) {
            text = text.replaceAll(Pattern.quote(String.format("\"%s\"", preset.getTypeName())),
                    Matcher.quoteReplacement(preset.getValue()));
        }

        // Replace custom presets
        for (PresetType preset : PresetManager.getCustomPresets(themeName)) {
            text = text.replaceAll(Pattern.quote(String.format("\"%s\"", preset.getTypeName())),
                    Matcher.quoteReplacement(preset.getValue()));
        }

        // Replace presets from the current collection
        if (collectionName != null) {
            for (PresetType preset : PresetManager.getGlobalPresets(collectionName, null)) {
                text = text.replaceAll(Pattern.quote(String.format("\"%s\"", preset.getTypeName())),
                        Matcher.quoteReplacement(PresetManager.resolvePresets(themeName, preset.getValue())));
            }
        }

        // Replace presets and images from resource banks
        String resourceBankName = themeDescriptor.getResourceBankName();
        if (resourceBankName != null) {
            ResourceBank resourceBank;
            try {
                resourceBank = ThemeManager.getResourceBank(resourceBankName);

                for (PresetInfo preset : resourceBank.getPresets()) {
                    text = text.replaceAll(Pattern.quote(String.format("\"%s\"", preset.getTypeName())),
                            Matcher.quoteReplacement(PresetManager.resolvePresets(themeName, preset.getValue())));
                }

                for (ImageInfo image : resourceBank.getImages()) {
                    String path = image.getPath();
                    text = text.replaceAll(path, Matcher.quoteReplacement(String.format("%s/nxthemes-images/%s/%s",
                            contextPath, resourceBankName, path.replace(" ", "%20"))));
                }

            } catch (ThemeException e) {
                log.warn("Could not get resources from theme bank: " + resourceBankName);
            }
        }

        return text;
    }
}