/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library 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 library 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.
*/
package com.liferay.sass.compiler.jni.internal;
import com.liferay.sass.compiler.SassCompiler;
import com.liferay.sass.compiler.jni.internal.libsass.LiferaysassLibrary;
import com.liferay.sass.compiler.jni.internal.libsass.LiferaysassLibrary.Sass_Context;
import com.liferay.sass.compiler.jni.internal.libsass.LiferaysassLibrary.Sass_File_Context;
import com.liferay.sass.compiler.jni.internal.libsass.LiferaysassLibrary.Sass_Options;
import com.liferay.sass.compiler.jni.internal.libsass.LiferaysassLibrary.Sass_Output_Style;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.file.Files;
import org.bridj.Platform;
import org.bridj.Pointer;
/**
* @author Gregory Amerson
* @author David Truong
*/
public class JniSassCompiler implements SassCompiler {
public JniSassCompiler() {
this(_PRECISION_DEFAULT);
}
public JniSassCompiler(int precision) {
this(precision, System.getProperty("java.io.tmpdir"));
}
public JniSassCompiler(int precision, String tmpDirName) {
Platform.addEmbeddedLibraryResourceRoot("/");
_precision = precision;
_tmpDirName = tmpDirName;
}
@Override
public String compileFile(String inputFileName, String includeDirName)
throws JniSassCompilerException {
return compileFile(inputFileName, includeDirName, false, "");
}
@Override
public String compileFile(
String inputFileName, String includeDirName,
boolean generateSourceMap)
throws JniSassCompilerException {
return compileFile(
inputFileName, includeDirName, generateSourceMap, "");
}
@Override
public String compileFile(
String inputFileName, String includeDirName,
boolean generateSourceMap, String sourceMapFileName)
throws JniSassCompilerException {
Pointer<Sass_File_Context> sassFileContextPointer = null;
try {
File inputFile = new File(inputFileName);
String includeDirNames =
includeDirName + File.pathSeparator + inputFile.getParent();
if ((sourceMapFileName == null) || sourceMapFileName.equals("")) {
sourceMapFileName = getOutputFileName(inputFileName) + ".map";
}
sassFileContextPointer = createSassFileContext(
inputFileName, includeDirNames, generateSourceMap,
sourceMapFileName);
Pointer<Sass_Context> sassContextPointer =
LiferaysassLibrary.sassFileContextGetContext(
sassFileContextPointer);
int errorStatus = LiferaysassLibrary.sassContextGetErrorStatus(
sassContextPointer);
if (errorStatus != 0) {
Pointer<Byte> errorMessagePointer =
LiferaysassLibrary.sassContextGetErrorMessage(
sassContextPointer);
throw new JniSassCompilerException(
errorMessagePointer.getCString());
}
Pointer<Byte> outputPointer =
LiferaysassLibrary.sassContextGetOutputString(
sassContextPointer);
if (generateSourceMap) {
try {
File sourceMapFile = new File(sourceMapFileName);
Pointer<Byte> sourceMapOutputPointer =
LiferaysassLibrary.sassContextGetSourceMapString(
sassContextPointer);
write(sourceMapFile, sourceMapOutputPointer.getCString());
}
catch (Exception e) {
System.out.println("Unable to create source map");
}
}
if (outputPointer == null) {
throw new JniSassCompilerException("Null output");
}
return outputPointer.getCString();
}
finally {
try {
if (sassFileContextPointer != null) {
LiferaysassLibrary.sassDeleteFileContext(
sassFileContextPointer);
}
}
catch (Throwable t) {
throw new JniSassCompilerException(t);
}
}
}
@Override
public String compileString(String input, String includeDirName)
throws JniSassCompilerException {
return compileString(input, "", includeDirName, false);
}
@Override
public String compileString(
String input, String inputFileName, String includeDirName,
boolean generateSourceMap)
throws JniSassCompilerException {
return compileString(
input, inputFileName, includeDirName, generateSourceMap, "");
}
@Override
public String compileString(
String input, String inputFileName, String includeDirName,
boolean generateSourceMap, String sourceMapFileName)
throws JniSassCompilerException {
try {
if ((inputFileName == null) || inputFileName.equals("")) {
inputFileName = _tmpDirName + File.separator + "tmp.scss";
if (generateSourceMap) {
System.out.println("Source maps require a valid file name");
generateSourceMap = false;
}
}
int index = inputFileName.lastIndexOf(File.separatorChar);
if ((index == -1) && (File.separatorChar != '/')) {
index = inputFileName.lastIndexOf('/');
}
index += 1;
String dirName = inputFileName.substring(0, index);
String fileName = inputFileName.substring(index);
String outputFileName = getOutputFileName(fileName);
if ((sourceMapFileName == null) || sourceMapFileName.equals("")) {
sourceMapFileName = dirName + outputFileName + ".map";
}
File tempFile = new File(dirName, "tmp.scss");
tempFile.deleteOnExit();
write(tempFile, input);
String output = compileFile(
tempFile.getCanonicalPath(), includeDirName, generateSourceMap,
sourceMapFileName);
if (generateSourceMap) {
File sourceMapFile = new File(sourceMapFileName);
String sourceMapContent = new String(
Files.readAllBytes(sourceMapFile.toPath()));
sourceMapContent = sourceMapContent.replaceAll(
"tmp\\.scss", fileName);
sourceMapContent = sourceMapContent.replaceAll(
"tmp\\.css", outputFileName);
write(sourceMapFile, sourceMapContent);
}
return output;
}
catch (Throwable t) {
throw new JniSassCompilerException(t);
}
}
protected Pointer<Sass_File_Context> createSassFileContext(
String inputFileName, String includeDirNames, boolean generateSourceMap,
String sourceMapFileName) {
Pointer<Sass_File_Context> sassFileContextPointer =
LiferaysassLibrary.sassMakeFileContext(toPointer(inputFileName));
Pointer<Sass_Options> sassOptionsPointer =
LiferaysassLibrary.sassMakeOptions();
LiferaysassLibrary.sassOptionSetIncludePath(
sassOptionsPointer, toPointer(includeDirNames));
LiferaysassLibrary.sassOptionSetInputPath(
sassOptionsPointer, toPointer(inputFileName));
LiferaysassLibrary.sassOptionSetOutputPath(
sassOptionsPointer, toPointer(""));
LiferaysassLibrary.sassOptionSetOutputStyle(
sassOptionsPointer, Sass_Output_Style.SASS_STYLE_NESTED);
LiferaysassLibrary.sassOptionSetPrecision(
sassOptionsPointer, _precision);
LiferaysassLibrary.sassOptionSetSourceComments(
sassOptionsPointer, false);
if (generateSourceMap) {
LiferaysassLibrary.sassOptionSetSourceMapContents(
sassOptionsPointer, false);
LiferaysassLibrary.sassOptionSetSourceMapEmbed(
sassOptionsPointer, false);
LiferaysassLibrary.sassOptionSetSourceMapFile(
sassOptionsPointer, toPointer(sourceMapFileName));
LiferaysassLibrary.sassOptionSetOmitSourceMapUrl(
sassOptionsPointer, false);
}
LiferaysassLibrary.sassFileContextSetOptions(
sassFileContextPointer, sassOptionsPointer);
LiferaysassLibrary.sassCompileFileContext(sassFileContextPointer);
return sassFileContextPointer;
}
protected String getOutputFileName(String fileName) {
return fileName.replaceAll("scss$", "css");
}
protected Pointer<Byte> toPointer(String s) {
return Pointer.pointerToCString(s);
}
protected void write(File file, String string) throws IOException {
if (!file.exists()) {
File parentFile = file.getParentFile();
parentFile.mkdirs();
file.createNewFile();
}
try (Writer writer = new OutputStreamWriter(
new FileOutputStream(file, false), "UTF-8")) {
writer.write(string);
}
}
private static final int _PRECISION_DEFAULT = 5;
private final int _precision;
private final String _tmpDirName;
}