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