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