All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.granite.ui.clientlibs.script.Utils Maven / Gradle / Ivy

/*************************************************************************
 *
 * ADOBE CONFIDENTIAL
 * __________________
 *
 *  Copyright 2011 Adobe Systems Incorporated
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Adobe Systems Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Adobe Systems Incorporated and its
 * suppliers and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Adobe Systems Incorporated.
 **************************************************************************/
package com.adobe.granite.ui.clientlibs.script;

import java.io.StringWriter;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.jcr.Binary;
import javax.jcr.Property;
import javax.jcr.Session;

import org.apache.jackrabbit.util.Base64;
import org.apache.jackrabbit.util.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The CssFileBuilder provides all specific css builder functionality.
 */
public final class Utils {

    /**
     * default logger
     */
    private static final Logger log = LoggerFactory.getLogger(Utils.class);

    /**
     * pattern that matches a scheme start, e.g. "http://"
     */
    private static Pattern SCHEME_START = Pattern.compile("^[^:/]+:[^/]*/.*");

    /**
     * pattern that matches an css url reference. eg: "background: url(image.jpg);"
     */
    private static Pattern URL_PATTERN= Pattern.compile("url\\(\\s*(['\"]?)([^'\")]*)(['\"]?\\s*)\\)");


    /**
     * static class
     */
    private Utils() {
    }

    /**
     * Resolves relative links within a css, such as an url to a background image, in respect to the
     * library path.
     *
     * @param libPath the library path.
     * @param filePath the path of the original css file
     * @param css the css to transform
     * @return the transformed css
     */
    public static String rewriteUrlsInCss(String libPath, String filePath, String css) {
        return rewriteUrlsInCss(Text.explode(libPath, '/'), Text.explode(filePath, '/'), css, null, 0);
    }

    /**
     * Resolves relative links within a css, such as an url to a background image, in respect to the
     * library path.
     *
     * @param libPath the library path.
     * @param filePath the path of the original css file
     * @param css the css to transform
     * @param session use to auto-inline data uris.
     * @param maxDataUriSize only inline data uris if smaller
     * @return the transformed css
     */
    public static String rewriteUrlsInCss(String libPath, String filePath, String css, Session session, long maxDataUriSize) {
        return rewriteUrlsInCss(Text.explode(libPath, '/'), Text.explode(filePath, '/'), css, session, maxDataUriSize);
    }

    /**
     * Resolves relative links within a css, such as an url to a background image, in respect to the
     * library path.
     *
     * @param libPathSegs the library path segments.
     * @param filePathSegs the path segments of the original css file
     * @param css the css to transform
     * @param session the session to use for embedding the data
     * @param maxDataUriSize  the max size for data uris
     * @return the transformed css
     */
    public static String rewriteUrlsInCss(String[] libPathSegs, String[] filePathSegs, String css, Session session, long maxDataUriSize) {
        Matcher m = URL_PATTERN.matcher(css);
        StringBuffer result = new StringBuffer();
        while (m.find()) {
            String url = m.group(2);
            if (url.startsWith("absolute:")) {
                url = url.substring(9);
            } else if (url.endsWith(".htc") || url.startsWith("//")) {
                // do nothing
            } else {
                url = resolveUrl(libPathSegs, filePathSegs, url);
                if (maxDataUriSize > 0 && session != null && !SCHEME_START.matcher(url).matches()) {
                    // check if referenced data exists and is smaller than max size
                    Binary bin = null;
                    String path = new StringBuilder("/")
                            .append(Text.implode(libPathSegs, "/"))
                            .append("/../")
                            .append(url)
                            .append("/jcr:content/jcr:data")
                            .toString();
                    try {
                        if (session.propertyExists(path)) {
                            Property p = session.getProperty(path);
                            if (p.getLength() < maxDataUriSize) {
                                bin = p.getBinary();
                                StringWriter w = new StringWriter();
                                Base64.encode(bin.getStream(), w);
                                url = new StringBuilder("data:")
                                        .append(p.getParent().getProperty(Property.JCR_MIMETYPE).getString())
                                        .append(";base64,")
                                        .append(w.toString())
                                        .toString();
                            }
                        }
                    } catch (Exception e) {
                        log.warn("Error while encoding data uri of {}: {}", path, e.toString());
                    } finally {
                        if (bin != null) {
                            bin.dispose();
                        }
                    }
                }
            }
            m.appendReplacement(result, "url($1" + url + "$3)");
        }
        m.appendTail(result);
        return result.toString();
    }
    
    /**
     * Resolves a relative link from a css style, such as an url to a background image, in respect to the
     * library path. If the given url is absolute, it is returned unchanged.
     *
     * Note that the libPath and filePath need to be canonical in order to work correctly.
     *
     * @param libPath the library path.
     * @param filePath the path of the original css file
     * @param url the url referenced in the css file
     * @return the resolved url
     */
    public static String resolveUrl(String libPath, String filePath, String url) {
        return resolveUrl(Text.explode(libPath, '/'), Text.explode(filePath, '/'), url);
    }
    
    /**
     * Resolves a relative link from a css style, such as an url to a background image, in respect to the
     * library path. If the given url is absolute it is returned unchanged.
     *
     * Note that the libPath and filePath need to be canonical in order to work correctly.
     *
     * @param libPath the library path.
     * @param filePath the path of the original css file
     * @param url the url referenced in the css file
     * @return the resolved url
     */
    private static String resolveUrl(String[] libPath, String[] filePath, String url) {
        // ignore empty, path absolute or absolute urls
        if (url.length() == 0 || SCHEME_START.matcher(url).matches()) {
            if (log.isDebugEnabled()) {
                log.debug("resolving lib=/{}, file=/{}, url={} (ignored)", new Object[]{
                        Text.implode(libPath, "/"), Text.implode(filePath, "/"), url
                });
            }
            return url;
        }
        // resolve url in respect to css file
        LinkedList file = new LinkedList();
        if (url.startsWith("/")) {
            // if absolute, we start with an empty 'CSS' file
        } else {
            file.addAll(Arrays.asList(filePath));
            // remove last segment as the urls are relative to the file's directory
            file.removeLast();
        }
        boolean warned = false;
        for (String seg: Text.explode(url, '/')) {
            if ("..".equals(seg)) {
                if (file.isEmpty()) {
                    if (!warned) {
                        log.warn("/{}: url('{}') invalid. too many '..'", Text.implode(filePath, "/"), url);
                        warned = true;
                    }
                } else {
                    file.removeLast();
                }
            } else if (".".equals(seg)) {
                // skip
            } else {
                file.add(seg);
            }
        }
        // make relative path in respect to library path. first remove the segments that are equal
        int i=0;
        while (i < libPath.length -1 && libPath[i].matches(file.getFirst())) {
            file.removeFirst();
            i++;
        }
        // then add parent segments up to the root
        while (i++ < libPath.length - 1) {
            file.addFirst("..");
        }
        // build resulting path
        StringBuilder ret = new StringBuilder();
        String delim = "";
        for (String seg: file) {
            ret.append(delim).append(seg);
            delim = "/";
        }
        if (log.isDebugEnabled()) {
            log.debug("resolving lib=/{}, file=/{}, url={} -> {}", new Object[]{
                    Text.implode(libPath, "/"), Text.implode(filePath, "/"), url, ret
            });
        }
        return ret.toString();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy