001/* 002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * <a href="mailto:grenard@nuxeo.com">Guillaume Renard</a> 016 * 017 */ 018 019package org.nuxeo.ecm.web.resources.wro.processor; 020 021import java.io.ByteArrayInputStream; 022import java.io.IOException; 023import java.io.InputStreamReader; 024import java.io.Reader; 025import java.io.Writer; 026import java.util.Arrays; 027import java.util.List; 028import org.apache.commons.io.IOUtils; 029import org.apache.commons.lang.ArrayUtils; 030import org.apache.commons.lang3.StringUtils; 031import org.apache.commons.logging.Log; 032import org.apache.commons.logging.LogFactory; 033import org.nuxeo.runtime.api.Framework; 034import org.nuxeo.theme.styling.service.ThemeStylingService; 035import org.nuxeo.theme.styling.service.descriptors.FlavorDescriptor; 036import org.nuxeo.theme.styling.service.descriptors.SassImport; 037import org.w3c.css.sac.InputSource; 038 039import com.vaadin.sass.internal.ScssStylesheet; 040import com.vaadin.sass.internal.handler.SCSSDocumentHandlerImpl; 041import com.vaadin.sass.internal.handler.SCSSErrorHandler; 042import com.vaadin.sass.internal.parser.ParseException; 043import com.vaadin.sass.internal.parser.Parser; 044import com.vaadin.sass.internal.parser.SCSSParseException; 045import com.vaadin.sass.internal.tree.Node; 046 047import ro.isdc.wro.WroRuntimeException; 048import ro.isdc.wro.model.resource.Resource; 049import ro.isdc.wro.model.resource.ResourceType; 050import ro.isdc.wro.model.resource.SupportedResourceType; 051 052/** 053 * Use Sass css processor to replace variables, mixin, etc. according to a given flavor. 054 * 055 * @since 7.4 056 */ 057@SupportedResourceType(ResourceType.CSS) 058public class SassCssFlavorProcessor extends AbstractFlavorProcessor { 059 060 private static final Log log = LogFactory.getLog(SassCssFlavorProcessor.class); 061 062 public static final String ALIAS = "sassCss"; 063 064 @Override 065 public void process(final Resource resource, final Reader reader, final Writer writer, String flavorName) 066 throws IOException { 067 if (isEnabled(resource)) { 068 Reader finalReader = null; 069 try { 070 String varContents = ""; 071 if (flavorName != null) { 072 ThemeStylingService s = Framework.getService(ThemeStylingService.class); 073 FlavorDescriptor fd = s.getFlavor(flavorName); 074 if (fd != null) { 075 List<SassImport> sassVars = fd.getSassImports(); 076 if (sassVars != null) { 077 for (SassImport var : sassVars) { 078 varContents += var.getContent(); 079 } 080 } 081 } 082 } 083 084 InputSource source = null; 085 if (StringUtils.isNoneBlank(varContents)) { 086 byte[] varBytes = varContents.getBytes(); 087 byte[] initalBytes = IOUtils.toByteArray(reader); 088 reader.close(); 089 byte[] finalBytes = ArrayUtils.addAll(varBytes, initalBytes); 090 finalReader = new InputStreamReader(new ByteArrayInputStream(finalBytes)); 091 } else { 092 finalReader = reader; 093 } 094 source = new InputSource(finalReader); 095 source.setEncoding(getEncoding()); 096 SCSSDocumentHandlerImpl scssDocumentHandlerImpl = new SCSSDocumentHandlerImpl(); 097 ScssStylesheet stylesheet = scssDocumentHandlerImpl.getStyleSheet(); 098 099 Parser parser = new Parser(); 100 parser.setErrorHandler(new SCSSErrorHandler()); 101 parser.setDocumentHandler(scssDocumentHandlerImpl); 102 103 try { 104 parser.parseStyleSheet(source); 105 } catch (ParseException e) { 106 log.error("Error while parsing resource " + resource.getUri(), e); 107 throw WroRuntimeException.wrap(new SCSSParseException(e, resource.getUri())); 108 } 109 110 stylesheet.setCharset(getEncoding()); 111 stylesheet.addSourceUris(Arrays.asList(resource.getUri())); 112 113 stylesheet.compile(); 114 115 StringBuilder string = new StringBuilder(""); 116 String delimeter = "\n\n"; 117 List<Node> children = stylesheet.getChildren(); 118 if (children.size() > 0) { 119 string.append(ScssStylesheet.PRINT_STRATEGY.build(children.get(0))); 120 } 121 if (children.size() > 1) { 122 for (int i = 1; i < children.size(); i++) { 123 String childString = ScssStylesheet.PRINT_STRATEGY.build(children.get(i)); 124 if (childString != null) { 125 string.append(delimeter).append(childString); 126 } 127 } 128 } 129 130 String content = string.toString(); 131 132 writer.write(content); 133 writer.flush(); 134 if (finalReader != null) { 135 finalReader.close(); 136 } 137 } catch (final Exception e) { 138 log.error("Error while serving resource " + resource.getUri(), e); 139 throw WroRuntimeException.wrap(e); 140 } finally { 141 IOUtils.closeQuietly(finalReader); 142 } 143 144 } else { 145 IOUtils.copy(reader, writer); 146 } 147 } 148 149 @Override 150 public String getAlias() { 151 return ALIAS; 152 } 153 154}