001
014
015 package com.liferay.portal.tools.sass;
016
017 import com.liferay.portal.kernel.util.CharPool;
018 import com.liferay.portal.kernel.util.FileUtil;
019 import com.liferay.portal.kernel.util.StringBundler;
020 import com.liferay.portal.kernel.util.StringPool;
021 import com.liferay.portal.kernel.util.StringUtil;
022 import com.liferay.portal.kernel.util.Validator;
023 import com.liferay.portal.servlet.filters.dynamiccss.RTLCSSUtil;
024 import com.liferay.portal.tools.SassToCssBuilder;
025 import com.liferay.portal.util.AggregateUtil;
026
027 import java.io.File;
028
029 import java.util.ArrayList;
030 import java.util.List;
031 import java.util.concurrent.Callable;
032
033
037 public class SassFile implements Callable<Void>, SassFragment {
038
039 public SassFile(String docrootDirName, String fileName) {
040 _docrootDirName = docrootDirName;
041 _fileName = fileName;
042
043 int pos = fileName.lastIndexOf(CharPool.SLASH);
044
045 if (pos != -1) {
046 _baseDir = fileName.substring(0, pos + 1);
047 }
048 else {
049 _baseDir = StringPool.BLANK;
050 }
051 }
052
053 @Override
054 public Void call() throws Exception {
055 long start = System.currentTimeMillis();
056
057 File file = new File(_docrootDirName, _fileName);
058
059 if (!file.exists()) {
060 return null;
061 }
062
063 String content = FileUtil.read(file);
064
065 int pos = 0;
066
067 StringBundler sb = new StringBundler();
068
069 while (true) {
070 int commentX = content.indexOf(_CSS_COMMENT_BEGIN, pos);
071 int commentY = content.indexOf(
072 _CSS_COMMENT_END, commentX + _CSS_COMMENT_BEGIN.length());
073
074 int importX = content.indexOf(_CSS_IMPORT_BEGIN, pos);
075 int importY = content.indexOf(
076 _CSS_IMPORT_END, importX + _CSS_IMPORT_BEGIN.length());
077
078 if ((importX == -1) || (importY == -1)) {
079 sb.append(content.substring(pos));
080
081 break;
082 }
083 else if ((commentX != -1) && (commentY != -1) &&
084 (commentX < importX) && (commentY > importX)) {
085
086 commentY += _CSS_COMMENT_END.length();
087
088 sb.append(content.substring(pos, commentY));
089
090 pos = commentY;
091 }
092 else {
093 sb.append(content.substring(pos, importX));
094
095 String mediaQuery = StringPool.BLANK;
096
097 int mediaQueryImportX = content.indexOf(
098 CharPool.CLOSE_PARENTHESIS,
099 importX + _CSS_IMPORT_BEGIN.length());
100 int mediaQueryImportY = content.indexOf(
101 CharPool.SEMICOLON, importX + _CSS_IMPORT_BEGIN.length());
102
103 String importFileName = null;
104
105 if (importY != mediaQueryImportX) {
106 mediaQuery = content.substring(
107 mediaQueryImportX + 1, mediaQueryImportY);
108
109 importFileName = content.substring(
110 importX + _CSS_IMPORT_BEGIN.length(),
111 mediaQueryImportX);
112 }
113 else {
114 importFileName = content.substring(
115 importX + _CSS_IMPORT_BEGIN.length(), importY);
116 }
117
118 if (!importFileName.isEmpty()) {
119 if (importFileName.charAt(0) != CharPool.SLASH) {
120 importFileName = _fixRelativePath(
121 _baseDir.concat(importFileName));
122 }
123
124 SassFile importSassFile = SassExecutorUtil.execute(
125 _docrootDirName, importFileName);
126
127 if (Validator.isNotNull(mediaQuery)) {
128 _sassFragments.add(
129 new SassFileWithMediaQuery(
130 importSassFile, mediaQuery));
131 }
132 else {
133 _sassFragments.add(importSassFile);
134 }
135 }
136
137
138
139 if (Validator.isNotNull(mediaQuery)) {
140 pos = mediaQueryImportY + 1;
141 }
142 else {
143 pos = importY + _CSS_IMPORT_END.length();
144 }
145 }
146 }
147
148 _addSassString(_fileName, sb.toString());
149
150 String rtlCustomFileName = SassToCssBuilder.getRtlCustomFileName(
151 _fileName);
152
153 File rtlCustomFile = new File(_docrootDirName, rtlCustomFileName);
154
155 if (rtlCustomFile.exists()) {
156 _addSassString(rtlCustomFileName, FileUtil.read(rtlCustomFile));
157 }
158
159 _elapsedTime = System.currentTimeMillis() - start;
160
161 return null;
162 }
163
164 @Override
165 public String getLtrContent() {
166 if (_ltrContent != null) {
167 return _ltrContent;
168 }
169
170 StringBundler sb = new StringBundler(_sassFragments.size());
171
172 for (SassFragment sassFragment : _sassFragments) {
173 String ltrContent = sassFragment.getLtrContent();
174
175 if (sassFragment instanceof SassFile) {
176 SassFile sassFile = (SassFile)sassFragment;
177
178 String baseURL = _BASE_URL.concat(sassFile._baseDir);
179
180 ltrContent = AggregateUtil.updateRelativeURLs(
181 ltrContent, baseURL);
182 }
183
184 sb.append(ltrContent);
185 }
186
187 _ltrContent = sb.toString();
188
189 return _ltrContent;
190 }
191
192 @Override
193 public String getRtlContent() {
194 if (_rtlContent != null) {
195 return _rtlContent;
196 }
197
198 StringBundler sb = new StringBundler(_sassFragments.size());
199
200 for (SassFragment sassFragment : _sassFragments) {
201 String rtlContent = sassFragment.getRtlContent();
202
203 if (sassFragment instanceof SassFile) {
204 SassFile sassFile = (SassFile)sassFragment;
205
206 String baseURL = _BASE_URL.concat(sassFile._baseDir);
207
208 rtlContent = AggregateUtil.updateRelativeURLs(
209 rtlContent, baseURL);
210 }
211
212 sb.append(rtlContent);
213 }
214
215 _rtlContent = sb.toString();
216
217 return _rtlContent;
218 }
219
220 @Override
221 public String toString() {
222 StringBundler sb = new StringBundler(5);
223
224 sb.append("Parsed ");
225 sb.append(_fileName);
226 sb.append(" in ");
227 sb.append(_elapsedTime);
228 sb.append("ms");
229
230 return sb.toString();
231 }
232
233 public void writeCacheFiles() throws Exception {
234 File ltrCacheFile = new File(
235 _docrootDirName,
236 SassToCssBuilder.getCacheFileName(_fileName, StringPool.BLANK));
237
238 FileUtil.write(ltrCacheFile, getLtrContent());
239
240 File ltrFile = new File(_docrootDirName, _fileName);
241
242 ltrCacheFile.setLastModified(ltrFile.lastModified());
243
244 String rtlFileName = SassToCssBuilder.getRtlCustomFileName(_fileName);
245
246 if (RTLCSSUtil.isExcludedPath(_fileName)) {
247 return;
248 }
249
250 File rtlCacheFile = new File(
251 _docrootDirName,
252 SassToCssBuilder.getCacheFileName(rtlFileName, StringPool.BLANK));
253
254 FileUtil.write(rtlCacheFile, getRtlContent());
255
256 rtlCacheFile.setLastModified(ltrFile.lastModified());
257 }
258
259 private void _addSassString(String fileName, String sassContent)
260 throws Exception {
261
262 sassContent = sassContent.trim();
263
264 if (sassContent.isEmpty()) {
265 return;
266 }
267
268 _sassFragments.add(new SassString(fileName, sassContent));
269 }
270
271 private String _fixRelativePath(String fileName) {
272 String[] paths = StringUtil.split(fileName, CharPool.SLASH);
273
274 StringBundler sb = new StringBundler(paths.length * 2);
275
276 for (String path : paths) {
277 if (path.isEmpty() || path.equals(StringPool.PERIOD)) {
278 continue;
279 }
280
281 if (path.equals(StringPool.DOUBLE_PERIOD) && (sb.length() >= 2)) {
282 sb.setIndex(sb.index() - 2);
283
284 continue;
285 }
286
287 sb.append(StringPool.SLASH);
288 sb.append(path);
289 }
290
291 return sb.toString();
292 }
293
294 private static final String _BASE_URL = "@base_url@";
295
296 private static final String _CSS_COMMENT_BEGIN = "";
299
300 private static final String _CSS_IMPORT_BEGIN = "@import url(";
301
302 private static final String _CSS_IMPORT_END = ");";
303
304 private String _baseDir;
305 private String _docrootDirName;
306 private long _elapsedTime;
307 private String _fileName;
308 private String _ltrContent;
309 private String _rtlContent;
310 private List<SassFragment> _sassFragments = new ArrayList<SassFragment>();
311
312 }