Skip to content

Commit 18412e2

Browse files
committed
Add support for option "rewrite-urls". see #54
1 parent b52bdf3 commit 18412e2

File tree

12 files changed

+276
-49
lines changed

12 files changed

+276
-49
lines changed

src/com/inet/lib/less/CssFormatter.java

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* MIT License (MIT)
33
*
4-
* Copyright (c) 2014 - 2018 Volker Berlin
4+
* Copyright (c) 2014 - 2019 Volker Berlin
55
*
66
* Permission is hereby granted, free of charge, to any person obtaining a copy
77
* of this software and associated documentation files (the "Software"), to deal
@@ -118,7 +118,11 @@ private static class SharedState {
118118

119119
private ReaderFactory readerFactory;
120120

121-
private CssOutput currentOutput;
121+
private Map<String, String> options;
122+
123+
private int rewriteUrl;
124+
125+
private CssOutput currentOutput;
122126

123127
private final static char[] DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
124128

@@ -180,10 +184,14 @@ private CssFormatter copy( @Nullable StringBuilder output ) {
180184
* A factory for the readers for imports.
181185
* @param target
182186
* the output of the resulting string
187+
* @param options
188+
* some optional options, see constants for details
183189
*/
184-
void format( LessParser parser, URL baseURL, ReaderFactory readerFactory, StringBuilder target ) {
190+
void format( LessParser parser, URL baseURL, ReaderFactory readerFactory, StringBuilder target, @Nonnull Map<String, String> options ) {
185191
state.baseURL = baseURL;
186192
this.readerFactory = readerFactory;
193+
this.options = options;
194+
this.rewriteUrl = parseRewriteUrl();
187195
addVariables( parser.getVariables() );
188196
state.isReference = false;
189197

@@ -216,6 +224,26 @@ void format( LessParser parser, URL baseURL, ReaderFactory readerFactory, String
216224
}
217225
}
218226

227+
/**
228+
* Parse the parameter rewrite URL
229+
*
230+
* @return 0, 1 or 2
231+
*/
232+
private int parseRewriteUrl() {
233+
String rewrite = options.get( Less.REWRITE_URLS );
234+
if( rewrite != null ) {
235+
switch( rewrite.toLowerCase() ) {
236+
case "off":
237+
return 0;
238+
case "local":
239+
return 1;
240+
case "all":
241+
return 2;
242+
}
243+
}
244+
return 0;
245+
}
246+
219247
/**
220248
* Get the formatter for CSS directives.
221249
* @return the header formatter
@@ -312,6 +340,18 @@ URL getBaseURL() {
312340
return state.baseURL;
313341
}
314342

343+
boolean isRewriteUrl( String url ) {
344+
switch( rewriteUrl ) {
345+
default:
346+
case 0:
347+
return false;
348+
case 1:
349+
return url.startsWith( "." );
350+
case 2:
351+
return true;
352+
}
353+
}
354+
315355
/**
316356
* Get the reader factory.
317357
* @return the factory

src/com/inet/lib/less/FunctionExpression.java

Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454

5555
import java.net.URI;
5656
import java.net.URISyntaxException;
57+
import java.net.URL;
5758
import java.nio.charset.StandardCharsets;
5859
import java.util.ArrayList;
5960
import java.util.List;
@@ -239,42 +240,17 @@ public void appendTo( CssFormatter formatter ) {
239240
return;
240241
case "url":
241242
String url = get( 1 ).stringValue( formatter );
242-
// char quoteChar = 0;
243-
// boolean quote = false;
244-
// if( url.length() >= 2 ) {
245-
// quoteChar = url.charAt( 0 );
246-
// if( quoteChar == '\'' || quoteChar == '\"' ) {
247-
// if( url.charAt( url.length() - 1 ) == quoteChar ) {
248-
// url = url.substring( 1, url.length() - 1 );
249-
// quote = true;
250-
// }
251-
// }
252-
// }
253-
// if( url.startsWith( "../" ) ) {
254-
// String baseUrl = get( 0 ).stringValue( formatter );
255-
// baseUrl = baseUrl.substring( 0, baseUrl.lastIndexOf( '/' ) + 1 );
256-
// boolean append = false;
257-
// do {
258-
// if( baseUrl.length() > 0 ) {
259-
// url = url.substring( 3 );
260-
// baseUrl = baseUrl.substring( 0, baseUrl.lastIndexOf( '/', baseUrl.length() - 2 ) + 1 );
261-
// append = true;
262-
// } else {
263-
// break;
264-
// }
265-
// } while( url.startsWith( "../" ) );
266-
// if( append ) {
267-
// url = baseUrl + url;
268-
// }
269-
// }
243+
String urlStr = UrlUtils.removeQuote( url );
244+
if( formatter.isRewriteUrl( urlStr ) ) {
245+
String relativeUrlStr = get( 0 ).stringValue( formatter );
246+
URL relativeUrl = new URL( "file", null, relativeUrlStr );
247+
relativeUrl = new URL( relativeUrl, urlStr );
248+
boolean quote = url != urlStr;
249+
urlStr = relativeUrl.getPath();
250+
url = quote ? url.charAt( 0 ) + urlStr + url.charAt( 0 ) : urlStr;
251+
}
270252
formatter.append( "url(" );
271-
// if( quote ) {
272-
// formatter.append( quoteChar );
273-
// }
274253
formatter.append( url );
275-
// if( quote ) {
276-
// formatter.append( quoteChar );
277-
// }
278254
formatter.append( ")" );
279255
return;
280256
case "data-uri":

src/com/inet/lib/less/Less.java

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* MIT License (MIT)
33
*
4-
* Copyright (c) 2014 - 2016 Volker Berlin
4+
* Copyright (c) 2014 - 2019 Volker Berlin
55
*
66
* Permission is hereby granted, free of charge, to any person obtaining a copy
77
* of this software and associated documentation files (the "Software"), to deal
@@ -32,12 +32,25 @@
3232
import java.net.URL;
3333
import java.nio.charset.StandardCharsets;
3434
import java.nio.file.Files;
35+
import java.util.Collections;
36+
import java.util.Map;
3537

3638
/**
3739
* The main class of JLessC library. Its contain all start points for converting LESS to CSS files.
3840
*/
3941
public class Less {
4042

43+
/**
44+
* Key for compress option. true, if the CSS data should be compressed without any extra formating characters.
45+
*/
46+
public static final String COMPRESS = "compress";
47+
48+
/**
49+
* Rewrites URLs to make them relative to the base less file. 'all' rewrites all URLs, 'local' just those starting
50+
* with a '.', 'off' simple inline it.
51+
*/
52+
public static final String REWRITE_URLS = "rewrite-urls";
53+
4154
/**
4255
* Compile the less data from a string.
4356
*
@@ -71,14 +84,55 @@ public static String compile( URL baseURL, String lessData, boolean compress ) t
7184
* if any error occur on compiling.
7285
*/
7386
public static String compile( URL baseURL, String lessData, boolean compress, ReaderFactory readerFactory ) throws LessException {
87+
Map<String,String> params = Collections.singletonMap( COMPRESS, Boolean.toString( compress ) );
88+
return compile( baseURL, lessData, params, readerFactory );
89+
}
90+
91+
/**
92+
* Compile the less data from a string.
93+
*
94+
* @param baseURL
95+
* the baseURL for import of external less data.
96+
* @param lessData
97+
* the input less data
98+
* @param options
99+
* some optional options, see constants for details
100+
* @return the resulting less data
101+
* @throws LessException
102+
* if any error occur on compiling.
103+
*/
104+
public static String compile( URL baseURL, String lessData, Map<String, String> options ) throws LessException {
105+
return compile( baseURL, lessData, options, new ReaderFactory() );
106+
}
107+
108+
/**
109+
* Compile the less data from a string.
110+
*
111+
* @param baseURL
112+
* the baseURL for import of external less data.
113+
* @param lessData
114+
* the input less data
115+
* @param options
116+
* some optional options, see constants for details
117+
* @param readerFactory
118+
* A factory for the readers for imports.
119+
* @return the resulting less data
120+
* @throws LessException
121+
* if any error occur on compiling.
122+
*/
123+
public static String compile( URL baseURL, String lessData, Map<String, String> options, ReaderFactory readerFactory ) throws LessException {
74124
try {
125+
if( options == null ) {
126+
options = Collections.emptyMap();
127+
}
75128
LessParser parser = new LessParser();
76129
parser.parse( baseURL, new StringReader( lessData ), readerFactory );
77-
78-
StringBuilder builder = new StringBuilder();
79-
CssFormatter formatter = compress ? new CompressCssFormatter() : new CssFormatter();
130+
131+
boolean compress = Boolean.parseBoolean( options.get( COMPRESS ) );
132+
CssFormatter formatter = compress ? new CompressCssFormatter() : new CssFormatter();
80133
parser.parseLazy( formatter );
81-
formatter.format( parser, baseURL, readerFactory, builder );
134+
StringBuilder builder = new StringBuilder();
135+
formatter.format( parser, baseURL, readerFactory, builder, options );
82136
return builder.toString();
83137
} catch( LessException ex ) {
84138
throw ex;

src/com/inet/lib/less/UrlUtils.java

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* MIT License (MIT)
33
*
4-
* Copyright (c) 2014 - 2015 Volker Berlin
4+
* Copyright (c) 2014 - 2019 Volker Berlin
55
*
66
* Permission is hereby granted, free of charge, to any person obtaining a copy
77
* of this software and associated documentation files (the "Software"), to deal
@@ -163,20 +163,40 @@ static double getColor( Expression param, CssFormatter formatter ) throws LessEx
163163
* Implementation of the function data-uri.
164164
*
165165
* @param formatter current formatter
166-
* @param relativeURL relative URL of the less script. Is used as base URL
166+
* @param relativeUrlStr relative URL of the less script. Is used as base URL
167167
* @param urlString the url parameter of the function
168168
* @param type the mime type
169169
* @throws IOException If any I/O errors occur on reading the content
170170
*/
171-
static void dataUri( CssFormatter formatter, String relativeURL, final String urlString, String type ) throws IOException {
172-
URL url = new URL( formatter.getBaseURL(), relativeURL );
171+
static void dataUri( CssFormatter formatter, String relativeUrlStr, final String urlString, String type ) throws IOException {
172+
URL url = formatter.getBaseURL();
173173
String urlStr = removeQuote( urlString );
174-
url = new URL( url, urlStr );
175174
InputStream input;
175+
url = new URL( url, urlStr );
176176
try {
177-
input = formatter.getReaderFactory().openStream( url );
177+
try {
178+
input = formatter.getReaderFactory().openStream( url );
179+
} catch( Exception e ) {
180+
// try rewrite location if option "rewrite-urls" is "all" or "local"
181+
if( formatter.isRewriteUrl( "." ) ) {
182+
url = new URL( new URL( formatter.getBaseURL(), relativeUrlStr ), urlStr );
183+
input = formatter.getReaderFactory().openStream( url );
184+
} else {
185+
throw e;
186+
}
187+
}
178188
} catch( Exception e ) {
179-
formatter.append( "url(" ).append( urlString ).append( ')' );
189+
boolean quote = urlString != urlStr;
190+
String rewrittenUrl;
191+
if( formatter.isRewriteUrl( urlStr ) ) {
192+
URL relativeUrl = new URL( relativeUrlStr );
193+
relativeUrl = new URL( relativeUrl, urlStr );
194+
rewrittenUrl = relativeUrl.getPath();
195+
rewrittenUrl = quote ? urlString.charAt( 0 ) + rewrittenUrl + urlString.charAt( 0 ) : rewrittenUrl;
196+
} else {
197+
rewrittenUrl = urlString;
198+
}
199+
formatter.append( "url(" ).append( rewrittenUrl ).append( ')' );
180200
return;
181201
}
182202
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/**
2+
* MIT License (MIT)
3+
*
4+
* Copyright (c) 2019 Volker Berlin
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* UT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*
24+
* @author Volker Berlin
25+
* @license: The MIT license <http://opensource.org/licenses/MIT>
26+
*/
27+
package com.inet.lib.less;
28+
29+
import static org.junit.Assert.assertEquals;
30+
31+
import java.io.IOException;
32+
import java.net.URL;
33+
import java.util.HashMap;
34+
import java.util.Scanner;
35+
36+
import org.junit.Test;
37+
38+
public class RewriteUrls {
39+
40+
private void test( String rewriteUrl ) throws IOException {
41+
Less less = new Less();
42+
43+
URL baseURL = getClass().getResource( "RewriteUrls/main.less" );
44+
45+
String lessData = new Scanner( baseURL.openStream(), "UTF-8" ).useDelimiter( "\\A" ).next();
46+
47+
HashMap<String, String> options = new HashMap<>();
48+
options.put( Less.REWRITE_URLS, rewriteUrl );
49+
50+
String expectedCss = new Scanner( new URL( baseURL, rewriteUrl + ".css" ).openStream(), "UTF-8" ).useDelimiter( "\\A" ).next();;
51+
52+
assertEquals( expectedCss, less.compile( baseURL, lessData, options ) );
53+
}
54+
55+
@Test
56+
public void off() throws IOException {
57+
test( "off" );
58+
}
59+
60+
@Test
61+
public void local() throws IOException {
62+
test( "local" );
63+
}
64+
65+
@Test
66+
public void all() throws IOException {
67+
test( "all" );
68+
}
69+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
.rule2 {
2+
url1: url('subfolder1/image.jpg');
3+
url2: url("data:image/jpeg;base64,bm90IGFjdHVhbGx5IGEganBlZyBmaWxlCg==");
4+
url3: url('subfolder1/subfolder2/subfolder1/image.jpg');
5+
url4: url("data:image/jpeg;base64,bm90IGFjdHVhbGx5IGEganBlZyBmaWxlCg==");
6+
url5: url('subfolder1/DoesNotExists.jpg');
7+
url4: url('subfolder1/subfolder2/subfolder1/DoesNotExists.jpg');
8+
}
9+
.rule1 {
10+
url1: url('subfolder1/image.jpg');
11+
url2: url("data:image/jpeg;base64,bm90IGFjdHVhbGx5IGEganBlZyBmaWxlCg==");
12+
url3: url('subfolder1/subfolder1/image.jpg');
13+
url4: url("data:image/jpeg;base64,bm90IGFjdHVhbGx5IGEganBlZyBmaWxlCg==");
14+
url5: url('subfolder1/DoesNotExists.jpg');
15+
url4: url('subfolder1/subfolder1/DoesNotExists.jpg');
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
.rule2 {
2+
url1: url('subfolder1/image.jpg');
3+
url2: url("data:image/jpeg;base64,bm90IGFjdHVhbGx5IGEganBlZyBmaWxlCg==");
4+
url3: url('subfolder1/image.jpg');
5+
url4: url("data:image/jpeg;base64,bm90IGFjdHVhbGx5IGEganBlZyBmaWxlCg==");
6+
url5: url('subfolder1/DoesNotExists.jpg');
7+
url4: url('subfolder1/DoesNotExists.jpg');
8+
}
9+
.rule1 {
10+
url1: url('image.jpg');
11+
url2: url("data:image/jpeg;base64,bm90IGFjdHVhbGx5IGEganBlZyBmaWxlCg==");
12+
url3: url('subfolder1/image.jpg');
13+
url4: url("data:image/jpeg;base64,bm90IGFjdHVhbGx5IGEganBlZyBmaWxlCg==");
14+
url5: url('DoesNotExists.jpg');
15+
url4: url('subfolder1/DoesNotExists.jpg');
16+
}

0 commit comments

Comments
 (0)