/* * JBoss, Home of Professional Open Source * Copyright 2013, Red Hat, Inc. and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.richfaces.resource.css; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import javax.faces.application.Resource; import javax.faces.context.FacesContext; import org.richfaces.el.util.ELUtils; import org.richfaces.log.Logger; import org.richfaces.log.RichfacesLogger; import org.w3c.dom.css.CSSCharsetRule; import org.w3c.dom.css.CSSFontFaceRule; import org.w3c.dom.css.CSSImportRule; import org.w3c.dom.css.CSSMediaRule; import org.w3c.dom.css.CSSPageRule; import org.w3c.dom.css.CSSRule; import org.w3c.dom.css.CSSStyleDeclaration; import org.w3c.dom.css.CSSStyleRule; import org.w3c.dom.css.CSSStyleSheet; import org.w3c.dom.css.CSSUnknownRule; import org.w3c.dom.stylesheets.MediaList; /** * @author Nick Belaevski * */ public final class CSSVisitorImpl extends AbstractCSSVisitor { private static final String RESOURCE_START_PREFIX = "resource["; private static final Logger LOGGER = RichfacesLogger.RESOURCE.getLogger(); private static final String NEW_LINE = "\r\n"; private FacesContext facesContext; private String encoding; private StringBuilder buffer = new StringBuilder(); private List<String> prefixes = new ArrayList<String>(2); public CSSVisitorImpl(FacesContext facesContext) { super(); this.facesContext = facesContext; } private void appendCSSText(CSSRule rule) { String cssText = rule.getCssText().trim(); // TODO nick - escape values if (cssText.length() != 0) { buffer.append(cssText); buffer.append(NEW_LINE); } } /* * private String escape(String cssText) { cssText = cssText.replaceAll("\\\\", "\\\\\\\\"); cssText = * cssText.replaceAll("\"", "\\\\\""); cssText = cssText.replaceAll("'", "\\\\'"); return cssText; } */ private void flushPrefixes() { if (!prefixes.isEmpty()) { for (String prefix : prefixes) { buffer.append(prefix); buffer.append(" {"); buffer.append(NEW_LINE); } prefixes.clear(); } } private void flushSuffix() { if (prefixes.isEmpty()) { buffer.append('}'); buffer.append(NEW_LINE); } else { prefixes.remove(prefixes.size() - 1); } } @Override public void visitUnknownRule(CSSUnknownRule rule) { appendCSSText(rule); } @Override public void visitCharsetRule(CSSCharsetRule rule) { encoding = rule.getEncoding(); appendCSSText(rule); } @Override public void visitImportRule(CSSImportRule rule) { // TODO nick - process imported stylesheet? String resourceName = rule.getHref(); String libraryName = null; if (ELUtils.isValueReference(resourceName)) { if (resourceName.indexOf(RESOURCE_START_PREFIX) == -1) { resourceName = facesContext.getApplication().evaluateExpressionGet(facesContext, resourceName, String.class); } else { int start = resourceName.indexOf(RESOURCE_START_PREFIX) + RESOURCE_START_PREFIX.length(); int end = resourceName.lastIndexOf("]"); resourceName = resourceName.substring(start, end); resourceName = resourceName.replaceAll("\"", "").replaceAll("'", "").trim(); } if (resourceName.contains(":")) { String[] split = resourceName.split(":", 2); libraryName = split[0]; resourceName = split[1]; } } Resource imported = facesContext.getApplication().getResourceHandler().createResource(resourceName, libraryName); if (imported == null) { LOGGER.error("Resource with name " + resourceName + " can't be found."); return; } String toAdd = null; try { toAdd = convertStreamToString(imported.getInputStream(), this.getEncoding()); } catch (IOException e) { LOGGER.error("Error while importing nested resource with name " + resourceName); } if (toAdd != null && toAdd.length() > 0) { buffer.append(toAdd); buffer.append(NEW_LINE); } else { appendCSSText(rule); } } @Override protected void startFontRule(CSSFontFaceRule rule) { prefixes.add("@font-face"); } @Override protected void endFontRule(CSSFontFaceRule rule) { flushSuffix(); } @Override protected void startMediaRule(CSSMediaRule rule) { MediaList mediaList = rule.getMedia(); // String mediaText = escape(mediaList.getMediaText()); String mediaText = mediaList.getMediaText(); prefixes.add("@media " + mediaText); } @Override protected void endMediaRule(CSSMediaRule rule) { flushSuffix(); } @Override protected void startPageRule(CSSPageRule rule) { // String selectorText = escape(rule.getSelectorText()); String selectorText = rule.getSelectorText(); // TODO nick - multiple selectors? prefixes.add("@page " + selectorText); } @Override protected void endPageRule(CSSPageRule rule) { flushSuffix(); } @Override protected void startStyleRule(CSSStyleRule rule) { // String selectorText = escape(rule.getSelectorText()); String selectorText = rule.getSelectorText(); prefixes.add(selectorText); } @Override protected void endStyleRule(CSSStyleRule rule) { flushSuffix(); } @Override protected void startStyleSheet(CSSStyleSheet styleSheet) { } @Override protected void endStyleSheet(CSSStyleSheet styleSheet) { } @Override public void visitStyleDeclaration(CSSStyleDeclaration styleDeclaration) { for (int j = 0; j < styleDeclaration.getLength(); j++) { String propertyName = styleDeclaration.item(j); String value = styleDeclaration.getPropertyValue(propertyName).trim(); String priority = styleDeclaration.getPropertyPriority(propertyName); if (ELUtils.isValueReference(value)) { value = facesContext.getApplication().evaluateExpressionGet(facesContext, value, String.class); } if (value.startsWith("\"") && value.endsWith("\"")) { value = value.substring(1, value.length() - 1); } if (value.startsWith("'") && value.endsWith("'")) { value = value.substring(1, value.length() - 1); } if (value.length() != 0 && !value.equals("\"\"") && !value.equals("''")) { flushPrefixes(); // One of properties of selector is not empty buffer.append('\t'); // buffer.append(escape(propertyName)); buffer.append(propertyName); buffer.append(": "); // buffer.append(escape(value)); buffer.append(value); if (priority != null && priority.length() != 0) { buffer.append(" !"); buffer.append(priority); } buffer.append(";"); buffer.append(NEW_LINE); } } } public String getEncoding() { return encoding; } public String getCSSText() { return buffer.toString(); } public void setEncoding(String encoding) { this.encoding = encoding; } public String convertStreamToString(InputStream is, String encoding) throws IOException { if (is != null) { StringBuilder sb = new StringBuilder(); String line; try { BufferedReader reader = new BufferedReader(new InputStreamReader(is, encoding)); while ((line = reader.readLine()) != null) { sb.append(line).append(NEW_LINE); } } finally { is.close(); } return sb.toString(); } else { return ""; } } }