/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* 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.xwiki.lesscss.internal.compiler;
import java.io.StringWriter;
import java.util.concurrent.Semaphore;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.xwiki.component.annotation.Component;
import org.xwiki.component.phase.Initializable;
import org.xwiki.component.phase.InitializationException;
import org.xwiki.lesscss.compiler.LESSCompilerException;
import org.xwiki.lesscss.internal.LESSConfiguration;
import org.xwiki.lesscss.internal.cache.CachedCompilerInterface;
import org.xwiki.lesscss.internal.compiler.less4j.Less4jCompiler;
import org.xwiki.lesscss.internal.resources.LESSSkinFileResourceReference;
import org.xwiki.lesscss.resources.LESSResourceReference;
import com.github.sommeri.less4j.Less4jException;
import com.xpn.xwiki.XWiki;
import com.xpn.xwiki.XWikiContext;
/**
* Compile a LESS resource in a particular context (@seeĀ org.xwiki.lesscss.compiler.IntegratedLESSCompiler}.
* To be used with AbstractCachedCompiler.
*
* @since 6.4M2
* @version $Id: 4c8cced8b7a9fcf5e6cb1e5513292ed9255d7e38 $
*/
@Component(roles = CachedLESSCompiler.class)
@Singleton
public class CachedLESSCompiler implements CachedCompilerInterface<String>, Initializable
{
/**
* The name of the file holding the main skin style (on which Velocity is always executed).
*/
public static final String MAIN_SKIN_STYLE_FILENAME = "style.less.vm";
private static final String SKIN_CONTEXT_KEY = "skin";
@Inject
private Provider<XWikiContext> xcontextProvider;
@Inject
private Less4jCompiler less4JCompiler;
@Inject
private LESSConfiguration lessConfiguration;
private Semaphore semaphore;
@Override
public void initialize() throws InitializationException
{
this.semaphore = new Semaphore(lessConfiguration.getMaximumSimultaneousCompilations(), true);
}
@Override
public String compute(LESSResourceReference lessResourceReference, boolean includeSkinStyle, boolean useVelocity,
boolean useLESS, String skin) throws LESSCompilerException
{
StringWriter source = new StringWriter();
try {
semaphore.acquire();
if (lessResourceReference instanceof LESSSkinFileResourceReference || includeSkinStyle) {
if (includeSkinStyle) {
// Add the import line to the LESS resource.
// We import this file to be able to use variables and mix-ins defined in it.
// But we don't want it in the output.
source.write(String.format("@import (reference) \"%s\";%s", MAIN_SKIN_STYLE_FILENAME,
System.lineSeparator()));
}
// Get the content of the LESS resource
source.write(lessResourceReference.getContent(skin));
}
// Parse the LESS content with Velocity
String lessCode = source.toString();
if (useVelocity) {
lessCode = executeVelocity(lessCode, skin);
}
// Compile the LESS code
if (useLESS) {
return less4JCompiler.compile(lessCode, skin, lessConfiguration.isGenerateInlineSourceMaps());
}
// Otherwise return the raw LESS code
return lessCode;
} catch (Less4jException | InterruptedException e) {
throw new LESSCompilerException(String.format("Failed to compile the resource [%s] with LESS.",
lessResourceReference), e);
} finally {
semaphore.release();
}
}
private String executeVelocity(String source, String skin)
{
// Get the XWiki object
XWikiContext xcontext = xcontextProvider.get();
XWiki xwiki = xcontext.getWiki();
String currentSkin = xwiki.getSkin(xcontext);
try {
// Trick: change the current skin in order to compile the LESS file as if the specified skin
// was the current skin
if (!currentSkin.equals(skin)) {
xcontext.put(SKIN_CONTEXT_KEY, skin);
}
return xwiki.evaluateVelocity(source, xcontext.getDoc().getPrefixedFullName());
} finally {
// Reset the current skin to the old value
if (!currentSkin.equals(skin)) {
xcontext.put(SKIN_CONTEXT_KEY, currentSkin);
}
}
}
}