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