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