001
014
015 package com.liferay.portal.tools;
016
017 import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayOutputStream;
018 import com.liferay.portal.kernel.io.unsync.UnsyncPrintWriter;
019 import com.liferay.portal.kernel.util.FastDateFormatFactoryUtil;
020 import com.liferay.portal.kernel.util.FileUtil;
021 import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
022 import com.liferay.portal.kernel.util.PropsUtil;
023 import com.liferay.portal.kernel.util.StringPool;
024 import com.liferay.portal.kernel.util.StringUtil;
025 import com.liferay.portal.kernel.util.SystemProperties;
026 import com.liferay.portal.kernel.util.UnsyncPrintWriterPool;
027 import com.liferay.portal.kernel.util.Validator;
028 import com.liferay.portal.model.ModelHintsConstants;
029 import com.liferay.portal.scripting.ruby.RubyExecutor;
030 import com.liferay.portal.servlet.filters.aggregate.AggregateFilter;
031 import com.liferay.portal.servlet.filters.aggregate.FileAggregateContext;
032 import com.liferay.portal.servlet.filters.dynamiccss.RTLCSSUtil;
033 import com.liferay.portal.util.FastDateFormatFactoryImpl;
034 import com.liferay.portal.util.FileImpl;
035 import com.liferay.portal.util.PortalImpl;
036 import com.liferay.portal.util.PortalUtil;
037 import com.liferay.portal.util.PropsImpl;
038
039 import java.io.File;
040
041 import java.util.ArrayList;
042 import java.util.HashMap;
043 import java.util.List;
044 import java.util.Map;
045
046 import org.apache.tools.ant.DirectoryScanner;
047
048
053 public class SassToCssBuilder {
054
055 public static File getCacheFile(String fileName) {
056 return getCacheFile(fileName, StringPool.BLANK);
057 }
058
059 public static File getCacheFile(String fileName, String suffix) {
060 return new File(getCacheFileName(fileName, suffix));
061 }
062
063 public static String getCacheFileName(String fileName, String suffix) {
064 String cacheFileName = StringUtil.replace(
065 fileName, StringPool.BACK_SLASH, StringPool.SLASH);
066
067 int x = cacheFileName.lastIndexOf(StringPool.SLASH);
068
069 int y = cacheFileName.lastIndexOf(StringPool.PERIOD);
070
071 if (y == -1) {
072 y = cacheFileName.length();
073 }
074
075 return cacheFileName.substring(0, x + 1) + ".sass-cache/" +
076 cacheFileName.substring(x + 1, y) + suffix +
077 cacheFileName.substring(y);
078 }
079
080 public static String getContent(String docrootDirName, String fileName)
081 throws Exception {
082
083 File file = new File(docrootDirName.concat(fileName));
084
085 String content = FileUtil.read(file);
086
087 content = AggregateFilter.aggregateCss(
088 new FileAggregateContext(docrootDirName, fileName), content);
089
090 return parseStaticTokens(content);
091 }
092
093 public static String getRtlCustomFileName(String fileName) {
094 int pos = fileName.lastIndexOf(StringPool.PERIOD);
095
096 return fileName.substring(0, pos) + "_rtl" + fileName.substring(pos);
097 }
098
099 public static void main(String[] args) throws Exception {
100 Map<String, String> arguments = ArgumentsUtil.parseArguments(args);
101
102 List<String> dirNames = new ArrayList<String>();
103
104 String dirName = arguments.get("sass.dir");
105
106 if (Validator.isNotNull(dirName)) {
107 dirNames.add(dirName);
108 }
109 else {
110 for (int i = 0;; i++ ) {
111 dirName = arguments.get("sass.dir." + i);
112
113 if (Validator.isNotNull(dirName)) {
114 dirNames.add(dirName);
115 }
116 else {
117 break;
118 }
119 }
120 }
121
122 String docrootDirName = arguments.get("sass.docroot.dir");
123 String portalCommonDirName = arguments.get("sass.portal.common.dir");
124
125 new SassToCssBuilder(dirNames, docrootDirName, portalCommonDirName);
126 }
127
128 public static String parseStaticTokens(String content) {
129 return StringUtil.replace(
130 content,
131 new String[] {
132 "@model_hints_constants_text_display_height@",
133 "@model_hints_constants_text_display_width@",
134 "@model_hints_constants_textarea_display_height@",
135 "@model_hints_constants_textarea_display_width@"
136 },
137 new String[] {
138 ModelHintsConstants.TEXT_DISPLAY_HEIGHT,
139 ModelHintsConstants.TEXT_DISPLAY_WIDTH,
140 ModelHintsConstants.TEXTAREA_DISPLAY_HEIGHT,
141 ModelHintsConstants.TEXTAREA_DISPLAY_WIDTH
142 });
143 }
144
145 public SassToCssBuilder(
146 List<String> dirNames, String docrootDirName,
147 String portalCommonDirName)
148 throws Exception {
149
150 Class<?> clazz = getClass();
151
152 ClassLoader classLoader = clazz.getClassLoader();
153
154 _initUtil(classLoader);
155
156 _rubyScript = StringUtil.read(
157 classLoader,
158 "com/liferay/portal/servlet/filters/dynamiccss" +
159 "/dependencies/main.rb");
160
161 _tempDir = SystemProperties.get(SystemProperties.TMP_DIR);
162
163 for (String dirName : dirNames) {
164
165
166
167
168 _rubyExecutor = new RubyExecutor();
169
170 _rubyExecutor.setExecuteInSeparateThread(false);
171
172 _parseSassDirectory(dirName, docrootDirName, portalCommonDirName);
173 }
174 }
175
176 private void _cacheSass(
177 String docrootDirName, String portalCommonDirName, String fileName)
178 throws Exception {
179
180 if (fileName.contains("_rtl") || RTLCSSUtil.isExcludedPath(fileName)) {
181 return;
182 }
183
184 File cacheFile = getCacheFile(docrootDirName.concat(fileName));
185
186 String parsedContent = _parseSassFile(
187 docrootDirName, portalCommonDirName, fileName);
188
189 FileUtil.write(cacheFile, parsedContent);
190
191 File file = new File(docrootDirName.concat(fileName));
192
193 long lastModified = file.lastModified();
194
195 cacheFile.setLastModified(lastModified);
196
197
198
199 File rtlCacheFile = getCacheFile(
200 docrootDirName.concat(fileName), "_rtl");
201
202 String rtlCss = RTLCSSUtil.getRtlCss(fileName, parsedContent);
203
204
205
206 String rtlCustomFileName = getRtlCustomFileName(fileName);
207
208 File rtlCustomFile = new File(docrootDirName, rtlCustomFileName);
209
210 if (rtlCustomFile.exists()) {
211 lastModified = rtlCustomFile.lastModified();
212
213 String rtlCustomCss = _parseSassFile(
214 docrootDirName, portalCommonDirName, rtlCustomFileName);
215
216 rtlCss += rtlCustomCss;
217 }
218
219 FileUtil.write(rtlCacheFile, rtlCss);
220
221 rtlCacheFile.setLastModified(lastModified);
222 }
223
224 private String _getCssThemePath(String fileName) {
225 int pos = fileName.lastIndexOf("/css/");
226
227 return fileName.substring(0, pos + 4);
228 }
229
230 private void _initUtil(ClassLoader classLoader) {
231 FastDateFormatFactoryUtil fastDateFormatFactoryUtil =
232 new FastDateFormatFactoryUtil();
233
234 fastDateFormatFactoryUtil.setFastDateFormatFactory(
235 new FastDateFormatFactoryImpl());
236
237 FileUtil fileUtil = new FileUtil();
238
239 fileUtil.setFile(new FileImpl());
240
241 PortalClassLoaderUtil.setClassLoader(classLoader);
242
243 PortalUtil portalUtil = new PortalUtil();
244
245 portalUtil.setPortal(new PortalImpl());
246
247 PropsUtil.setProps(new PropsImpl());
248
249 RTLCSSUtil.init();
250 }
251
252 private boolean _isModified(String dirName, String[] fileNames)
253 throws Exception {
254
255 for (String fileName : fileNames) {
256 if (fileName.contains("_rtl")) {
257 continue;
258 }
259
260 fileName = _normalizeFileName(dirName, fileName);
261
262 File file = new File(fileName);
263 File cacheFile = getCacheFile(fileName);
264
265 if (file.lastModified() != cacheFile.lastModified()) {
266 return true;
267 }
268 }
269
270 return false;
271 }
272
273 private String _normalizeFileName(String dirName, String fileName) {
274 return StringUtil.replace(
275 dirName + StringPool.SLASH + fileName,
276 new String[] {
277 StringPool.BACK_SLASH, StringPool.DOUBLE_SLASH
278 },
279 new String[] {
280 StringPool.SLASH, StringPool.SLASH
281 }
282 );
283 }
284
285 private void _parseSassDirectory(
286 String dirName, String docrootDirName, String portalCommonDirName)
287 throws Exception {
288
289 DirectoryScanner directoryScanner = new DirectoryScanner();
290
291 String basedir = docrootDirName.concat(dirName);
292
293 directoryScanner.setBasedir(basedir);
294
295 directoryScanner.setExcludes(
296 new String[] {
297 "**\\_diffs\\**", "**\\.sass-cache*\\**",
298 "**\\.sass_cache_*\\**", "**\\_sass_cache_*\\**",
299 "**\\_styled\\**", "**\\_unstyled\\**"
300 });
301 directoryScanner.setIncludes(new String[] {"**\\*.css"});
302
303 directoryScanner.scan();
304
305 String[] fileNames = directoryScanner.getIncludedFiles();
306
307 if (!_isModified(basedir, fileNames)) {
308 return;
309 }
310
311 for (String fileName : fileNames) {
312 fileName = _normalizeFileName(dirName, fileName);
313
314 try {
315 long start = System.currentTimeMillis();
316
317 _cacheSass(docrootDirName, portalCommonDirName, fileName);
318
319 long end = System.currentTimeMillis();
320
321 System.out.println(
322 "Parsed " + docrootDirName + fileName + " in " +
323 (end - start) + " ms");
324 }
325 catch (Exception e) {
326 System.out.println("Unable to parse " + fileName);
327
328 throw e;
329 }
330 }
331 }
332
333 private String _parseSassFile(
334 String docrootDirName, String portalCommonDirName, String fileName)
335 throws Exception {
336
337 String filePath = docrootDirName.concat(fileName);
338
339 Map<String, Object> inputObjects = new HashMap<String, Object>();
340
341 inputObjects.put("commonSassPath", portalCommonDirName);
342 inputObjects.put("content", getContent(docrootDirName, fileName));
343 inputObjects.put("cssRealPath", filePath);
344 inputObjects.put("cssThemePath", _getCssThemePath(filePath));
345 inputObjects.put("sassCachePath", _tempDir);
346
347 UnsyncByteArrayOutputStream unsyncByteArrayOutputStream =
348 new UnsyncByteArrayOutputStream();
349
350 UnsyncPrintWriter unsyncPrintWriter = UnsyncPrintWriterPool.borrow(
351 unsyncByteArrayOutputStream);
352
353 inputObjects.put("out", unsyncPrintWriter);
354
355 _rubyExecutor.eval(null, inputObjects, null, _rubyScript);
356
357 unsyncPrintWriter.flush();
358
359 return unsyncByteArrayOutputStream.toString();
360 }
361
362 private RubyExecutor _rubyExecutor;
363 private String _rubyScript;
364 private String _tempDir;
365
366 }