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.util.FastDateFormatFactoryImpl;
033 import com.liferay.portal.util.FileImpl;
034 import com.liferay.portal.util.PortalImpl;
035 import com.liferay.portal.util.PortalUtil;
036 import com.liferay.portal.util.PropsImpl;
037
038 import java.io.File;
039
040 import java.util.ArrayList;
041 import java.util.HashMap;
042 import java.util.List;
043 import java.util.Map;
044
045 import org.apache.tools.ant.DirectoryScanner;
046
047
052 public class SassToCssBuilder {
053
054 public static File getCacheFile(String fileName) {
055 fileName = StringUtil.replace(
056 fileName, StringPool.BACK_SLASH, StringPool.SLASH);
057
058 int pos = fileName.lastIndexOf(StringPool.SLASH);
059
060 String cacheFileName =
061 fileName.substring(0, pos + 1) + ".sass-cache/" +
062 fileName.substring(pos + 1);
063
064 return new File(cacheFileName);
065 }
066
067 public static void main(String[] args) {
068 Map<String, String> arguments = ArgumentsUtil.parseArguments(args);
069
070 String docrootDirName = arguments.get("saas.docroot.dir");
071
072 List<String> dirNames = new ArrayList<String>();
073
074 String dirName = arguments.get("sass.dir");
075
076 if (Validator.isNotNull(dirName)) {
077 dirNames.add(dirName);
078 }
079 else {
080 for (int i = 0;; i++ ) {
081 dirName = arguments.get("sass.dir." + i);
082
083 if (Validator.isNotNull(dirName)) {
084 dirNames.add(dirName);
085 }
086 else {
087 break;
088 }
089 }
090 }
091
092 try {
093 new SassToCssBuilder(docrootDirName, dirNames);
094 }
095 catch (Exception e) {
096 e.printStackTrace();
097 }
098 }
099
100 public static String parseStaticTokens(String content) {
101 return StringUtil.replace(
102 content,
103 new String[] {
104 "@model_hints_constants_text_display_height@",
105 "@model_hints_constants_text_display_width@",
106 "@model_hints_constants_textarea_display_height@",
107 "@model_hints_constants_textarea_display_width@"
108 },
109 new String[] {
110 ModelHintsConstants.TEXT_DISPLAY_HEIGHT,
111 ModelHintsConstants.TEXT_DISPLAY_WIDTH,
112 ModelHintsConstants.TEXTAREA_DISPLAY_HEIGHT,
113 ModelHintsConstants.TEXTAREA_DISPLAY_WIDTH
114 });
115 }
116
117 public SassToCssBuilder(String docrootDirName, List<String> dirNames)
118 throws Exception {
119
120 Class<?> clazz = getClass();
121
122 ClassLoader classLoader = clazz.getClassLoader();
123
124 _initUtil(classLoader);
125
126 _rubyScript = StringUtil.read(
127 classLoader,
128 "com/liferay/portal/servlet/filters/dynamiccss/main.rb");
129
130 _tempDir = SystemProperties.get(SystemProperties.TMP_DIR);
131
132 for (String dirName : dirNames) {
133
134
135
136
137 _rubyExecutor = new RubyExecutor();
138
139 _rubyExecutor.setExecuteInSeparateThread(false);
140
141 _parseSassDirectory(docrootDirName, dirName);
142 }
143 }
144
145 private String _getContent(String docrootDirName, String resourcePath)
146 throws Exception {
147
148 String filePath = docrootDirName.concat(resourcePath);
149
150 File file = new File(filePath);
151
152 String content = FileUtil.read(file);
153
154 content = AggregateFilter.aggregateCss(
155 new FileAggregateContext(docrootDirName, resourcePath), content);
156
157 return parseStaticTokens(content);
158 }
159
160 private String _getCssThemePath(String fileName) {
161 int pos = fileName.lastIndexOf("/css/");
162
163 return fileName.substring(0, pos + 4);
164 }
165
166 private void _initUtil(ClassLoader classLoader) {
167 FastDateFormatFactoryUtil fastDateFormatFactoryUtil =
168 new FastDateFormatFactoryUtil();
169
170 fastDateFormatFactoryUtil.setFastDateFormatFactory(
171 new FastDateFormatFactoryImpl());
172
173 FileUtil fileUtil = new FileUtil();
174
175 fileUtil.setFile(new FileImpl());
176
177 PortalClassLoaderUtil.setClassLoader(classLoader);
178
179 PortalUtil portalUtil = new PortalUtil();
180
181 portalUtil.setPortal(new PortalImpl());
182
183 PropsUtil.setProps(new PropsImpl());
184 }
185
186 private boolean _isModified(String dirName, String[] fileNames)
187 throws Exception {
188
189 for (String fileName : fileNames) {
190 fileName = _normalizeFileName(dirName, fileName);
191
192 File file = new File(fileName);
193 File cacheFile = getCacheFile(fileName);
194
195 if (file.lastModified() != cacheFile.lastModified()) {
196 return true;
197 }
198 }
199
200 return false;
201 }
202
203 private String _normalizeFileName(String dirName, String fileName) {
204 return StringUtil.replace(
205 dirName + StringPool.SLASH + fileName,
206 new String[] {
207 StringPool.BACK_SLASH, StringPool.DOUBLE_SLASH
208 },
209 new String[] {
210 StringPool.SLASH, StringPool.SLASH
211 }
212 );
213 }
214
215 private void _parseSassDirectory(String docrootDirName, String dirName)
216 throws Exception {
217
218 DirectoryScanner directoryScanner = new DirectoryScanner();
219
220 String basedir = docrootDirName.concat(dirName);
221
222 directoryScanner.setBasedir(basedir);
223
224 directoryScanner.setExcludes(
225 new String[] {
226 "**\\_diffs\\**", "**\\.sass-cache*\\**",
227 "**\\.sass_cache_*\\**", "**\\_sass_cache_*\\**",
228 "**\\_styled\\**", "**\\_unstyled\\**"
229 });
230 directoryScanner.setIncludes(new String[] {"**\\*.css"});
231
232 directoryScanner.scan();
233
234 String[] fileNames = directoryScanner.getIncludedFiles();
235
236 if (!_isModified(basedir, fileNames)) {
237 return;
238 }
239
240 for (String fileName : fileNames) {
241 fileName = _normalizeFileName(dirName, fileName);
242
243 try {
244 long start = System.currentTimeMillis();
245
246 _parseSassFile(docrootDirName, fileName);
247
248 long end = System.currentTimeMillis();
249
250 System.out.println(
251 "Parsed " + docrootDirName + fileName + " in " +
252 (end - start) + " ms");
253 }
254 catch (Exception e) {
255 System.out.println("Unable to parse " + fileName);
256
257 e.printStackTrace();
258 }
259 }
260 }
261
262 private void _parseSassFile(String docrootDirName, String resourcePath)
263 throws Exception {
264
265 String filePath = docrootDirName.concat(resourcePath);
266
267 File file = new File(filePath);
268 File cacheFile = getCacheFile(filePath);
269
270 Map<String, Object> inputObjects = new HashMap<String, Object>();
271
272 inputObjects.put("content", _getContent(docrootDirName, resourcePath));
273 inputObjects.put("cssRealPath", filePath);
274 inputObjects.put("cssThemePath", _getCssThemePath(filePath));
275 inputObjects.put("sassCachePath", _tempDir);
276
277 UnsyncByteArrayOutputStream unsyncByteArrayOutputStream =
278 new UnsyncByteArrayOutputStream();
279
280 UnsyncPrintWriter unsyncPrintWriter = UnsyncPrintWriterPool.borrow(
281 unsyncByteArrayOutputStream);
282
283 inputObjects.put("out", unsyncPrintWriter);
284
285 _rubyExecutor.eval(null, inputObjects, null, _rubyScript);
286
287 unsyncPrintWriter.flush();
288
289 String parsedContent = unsyncByteArrayOutputStream.toString();
290
291 FileUtil.write(cacheFile, parsedContent);
292
293 cacheFile.setLastModified(file.lastModified());
294 }
295
296 private RubyExecutor _rubyExecutor;
297 private String _rubyScript;
298 private String _tempDir;
299
300 }