Revision 45633
trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.lib/org.gvsig.expressionevaluator.lib.api/src/main/java/org/gvsig/expressionevaluator/ExpressionBuilder.java | ||
---|---|---|
131 | 131 |
|
132 | 132 |
public static final String FUNCTION_COLOR = "COLOR"; |
133 | 133 |
|
134 |
public static final String FUNCTION_JSON_VALUE = "JSON_VALUE"; |
|
135 |
|
|
134 | 136 |
public static final int PARAMETER_TYPE_CONSTANT = 0; |
135 | 137 |
public static final int PARAMETER_TYPE_VARIABLE = 1; |
136 | 138 |
|
trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.lib/org.gvsig.expressionevaluator.lib.api/src/main/java/org/gvsig/expressionevaluator/spi/AbstractFunction.java | ||
---|---|---|
14 | 14 |
import java.util.List; |
15 | 15 |
import java.util.Locale; |
16 | 16 |
import java.util.Objects; |
17 |
import java.util.logging.Level; |
|
18 |
import java.util.logging.Logger; |
|
19 |
import java.util.regex.Matcher; |
|
20 |
import java.util.regex.Pattern; |
|
17 | 21 |
import javax.json.JsonArray; |
18 | 22 |
import javax.json.JsonObject; |
19 | 23 |
import javax.json.JsonStructure; |
24 |
import org.apache.commons.io.FilenameUtils; |
|
20 | 25 |
import org.apache.commons.io.IOUtils; |
21 | 26 |
import org.apache.commons.lang3.BooleanUtils; |
22 | 27 |
import org.apache.commons.lang3.Range; |
... | ... | |
638 | 643 |
return; |
639 | 644 |
} |
640 | 645 |
} |
641 |
InputStream is = null; |
|
646 |
// InputStream is = null;
|
|
642 | 647 |
JSONObject json; |
643 | 648 |
try { |
644 |
is = url.openStream(); |
|
645 |
List<String> lines = IOUtils.readLines(is); |
|
646 |
json = new JSONObject(StringUtils.join(lines, "\n")); |
|
649 |
// is = url.openStream(); |
|
650 |
// List<String> lines = IOUtils.readLines(is); |
|
651 |
// json = new JSONObject(StringUtils.join(lines, "\n")); |
|
652 |
json = new JSONObject(IOUtils.toString(url)); |
|
647 | 653 |
} catch (Exception ex) { |
648 | 654 |
return; |
649 |
} finally { |
|
650 |
IOUtils.closeQuietly(is); |
|
655 |
// } finally {
|
|
656 |
// IOUtils.closeQuietly(is);
|
|
651 | 657 |
} |
652 | 658 |
|
653 | 659 |
if( json.has("group") ) { |
... | ... | |
669 | 675 |
} else { |
670 | 676 |
this.description = Objects.toString(x, null); |
671 | 677 |
} |
678 |
this.description = process_includes(url, this.description); |
|
672 | 679 |
this.description = StringUtils.replace( |
673 | 680 |
this.description, |
674 | 681 |
"@@@", |
... | ... | |
699 | 706 |
} |
700 | 707 |
} |
701 | 708 |
} |
709 |
|
|
710 |
private String process_includes(URL pathname, String text) { |
|
711 |
Pattern p1 = Pattern.compile("[<][%]include (?<fname>[a-zA-Z0-9_$.]+)[%][>]", Pattern.DOTALL); |
|
712 |
while(true) { |
|
713 |
Matcher m = p1.matcher(text); |
|
714 |
if( m==null || !m.find()) { |
|
715 |
return text; |
|
716 |
} |
|
717 |
String path = FilenameUtils.getPathNoEndSeparator(pathname.toString()); |
|
718 |
String fname = m.group("fname"); |
|
719 |
URL url; |
|
720 |
String replacement; |
|
721 |
try { |
|
722 |
url = new URL(path+"/"+fname); |
|
723 |
replacement = IOUtils.toString(url); |
|
724 |
// text = m.replaceAll(replacement); |
|
725 |
StringBuilder builder = new StringBuilder(text.length()+replacement.length()); |
|
726 |
builder.append(text.substring(0, m.start())); |
|
727 |
builder.append(replacement); |
|
728 |
builder.append(text.substring(m.end())); |
|
729 |
text = builder.toString(); |
|
730 |
} catch (Exception ex) { |
|
731 |
return text; |
|
732 |
} |
|
733 |
} |
|
734 |
} |
|
735 |
|
|
702 | 736 |
protected static final int TYPE_INT = 0b00000001; |
703 | 737 |
protected static final int TYPE_LONG = 0b00000010; |
704 | 738 |
protected static final int TYPE_FLOAT = 0b00000100; |
trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.lib/org.gvsig.expressionevaluator.lib.impl/src/main/resources/org/gvsig/expressionevaluator/functions/en/JSON_VALUE.json | ||
---|---|---|
1 |
{ |
|
2 |
"name": "JSON_VALUE", |
|
3 |
"group": "Conversion", |
|
4 |
"template": "JSON_VALUE({{exp}}, path)", |
|
5 |
"args": [ |
|
6 |
"exp - specify an expression that evaluates to a text literal. If expr is null, then the function returns null. If expr is not a text literal of well-formed JSON data using strict or lax syntax, then the function returns null by default.", |
|
7 |
"JsonPath - specify a JSON path expression. The function uses the path expression to evaluate expr and find a scalar JSON value that matches, or satisfies, the path expression. The path expression must be a text literal" |
|
8 |
], |
|
9 |
"description": "Extracts a scalar value from a JSON string.<br>\n<%include JSON_VALUE.description.html%>", |
|
10 |
"returnType": "Object", |
|
11 |
"sqlCompatible": false |
|
12 |
} |
trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.lib/org.gvsig.expressionevaluator.lib.impl/src/main/resources/org/gvsig/expressionevaluator/functions/en/JSON_VALUE.description.html | ||
---|---|---|
1 |
<p>JsonPath expressions always refer to a |
|
2 |
JSON structure in the same way as XPath |
|
3 |
expression are used in combination |
|
4 |
with an XML document. The "root member |
|
5 |
object" in JsonPath is always referred to |
|
6 |
as <code>$</code> regardless if it is an |
|
7 |
object or array.</p> |
|
8 |
<p>JsonPath expressions can use the |
|
9 |
dot–notation</p> |
|
10 |
<p><code>$.store.book[0].title</code></p> |
|
11 |
<p>or the bracket–notation</p> |
|
12 |
<p><code>$['store']['book'][0]['title']</code></p> |
|
13 |
<h2>Operators</h2> |
|
14 |
<table> |
|
15 |
<thead> |
|
16 |
<tr> |
|
17 |
<th align="left">Operator</th> |
|
18 |
<th align="left">Description</th> |
|
19 |
</tr> |
|
20 |
</thead> |
|
21 |
<tbody> |
|
22 |
<tr> |
|
23 |
<td align="left"><code>$</code></td> |
|
24 |
<td align="left">The root element to |
|
25 |
query. This starts all path |
|
26 |
expressions.</td> |
|
27 |
</tr> |
|
28 |
<tr> |
|
29 |
<td align="left"><code>@</code></td> |
|
30 |
<td align="left">The current node |
|
31 |
being processed by a filter |
|
32 |
predicate.</td> |
|
33 |
</tr> |
|
34 |
<tr> |
|
35 |
<td align="left"><code>*</code></td> |
|
36 |
<td align="left">Wildcard. Available |
|
37 |
anywhere a name or numeric are |
|
38 |
required.</td> |
|
39 |
</tr> |
|
40 |
<tr> |
|
41 |
<td align="left"><code>..</code></td> |
|
42 |
<td align="left">Deep scan. Available |
|
43 |
anywhere a name is required.</td> |
|
44 |
</tr> |
|
45 |
<tr> |
|
46 |
<td align="left"><code>.<name></code></td> |
|
47 |
<td align="left">Dot-notated child</td> |
|
48 |
</tr> |
|
49 |
<tr> |
|
50 |
<td align="left"><code>['<name>' |
|
51 |
(, '<name>')]</code></td> |
|
52 |
<td align="left">Bracket-notated child |
|
53 |
or children</td> |
|
54 |
</tr> |
|
55 |
<tr> |
|
56 |
<td align="left"><code>[<number> |
|
57 |
(, <number>)]</code></td> |
|
58 |
<td align="left">Array index or |
|
59 |
indexes</td> |
|
60 |
</tr> |
|
61 |
<tr> |
|
62 |
<td align="left"><code>[start:end]</code></td> |
|
63 |
<td align="left">Array slice operator</td> |
|
64 |
</tr> |
|
65 |
<tr> |
|
66 |
<td align="left"><code>[?(<expression>)]</code></td> |
|
67 |
<td align="left">Filter expression. Expression must evaluate to a boolean value.</td> |
|
68 |
</tr> |
|
69 |
</tbody> |
|
70 |
</table> |
|
71 |
<h2>Functions</h2> |
|
72 |
<p>Functions can be invoked at the tail end |
|
73 |
of a path - the input to a function is the |
|
74 |
output of the path expression. |
|
75 |
The function output is dictated by the |
|
76 |
function itself.</p> |
|
77 |
<table> |
|
78 |
<thead> |
|
79 |
<tr> |
|
80 |
<th align="left">Function</th> |
|
81 |
<th align="left">Description</th> |
|
82 |
<th align="left">Output type</th> |
|
83 |
</tr> |
|
84 |
</thead> |
|
85 |
<tbody> |
|
86 |
<tr> |
|
87 |
<td align="left">min()</td> |
|
88 |
<td align="left">Provides the min value of an array of numbers</td> |
|
89 |
<td align="left">Double</td> |
|
90 |
</tr> |
|
91 |
<tr> |
|
92 |
<td align="left">max()</td> |
|
93 |
<td align="left">Provides the max value of an array of numbers</td> |
|
94 |
<td align="left">Double</td> |
|
95 |
</tr> |
|
96 |
<tr> |
|
97 |
<td align="left">avg()</td> |
|
98 |
<td align="left">Provides the average value of an array of numbers</td> |
|
99 |
<td align="left">Double</td> |
|
100 |
</tr> |
|
101 |
<tr> |
|
102 |
<td align="left">stddev()</td> |
|
103 |
<td align="left">Provides the standard deviation value of an array of numbers</td> |
|
104 |
<td align="left">Double</td> |
|
105 |
</tr> |
|
106 |
<tr> |
|
107 |
<td align="left">length()</td> |
|
108 |
<td align="left">Provides the length of an array</td> |
|
109 |
<td align="left">Integer</td> |
|
110 |
</tr> |
|
111 |
<tr> |
|
112 |
<td align="left">sum()</td> |
|
113 |
<td align="left">Provides the sum value of an array of numbers</td> |
|
114 |
<td align="left">Double</td> |
|
115 |
</tr> |
|
116 |
<tr> |
|
117 |
<td align="left">keys()</td> |
|
118 |
<td align="left">Provides the property keys (An alternative for terminal tilde <code>~</code>)</td> |
|
119 |
<td align="left"><code>Set<E></code></td> |
|
120 |
</tr> |
|
121 |
<tr> |
|
122 |
<td align="left">concat(X)</td> |
|
123 |
<td align="left">Provides a concatinated version of the path output with a new item</td> |
|
124 |
<td align="left">like input</td> |
|
125 |
</tr> |
|
126 |
<tr> |
|
127 |
<td align="left">append(X)</td> |
|
128 |
<td align="left">add an item to the json path output array</td> |
|
129 |
<td align="left">like input</td> |
|
130 |
</tr> |
|
131 |
</tbody> |
|
132 |
</table> |
|
133 |
<h2>Filter Operators</h2> |
|
134 |
<p>Filters are logical expressions used to |
|
135 |
filter arrays. A typical filter would be <code>[?(@.age> 18)]</code> where <code>@</code> |
|
136 |
represents the current item being |
|
137 |
processed. More complex filters can be |
|
138 |
created with logical operators <code>&&</code> |
|
139 |
and <code>||</code>. String literals must |
|
140 |
be enclosed by single or double quotes (<code>[?(@.color |
|
141 |
== 'blue')]</code> or <code>[?(@.color |
|
142 |
== "blue")]</code>).</p> |
|
143 |
<table> |
|
144 |
<thead> |
|
145 |
<tr> |
|
146 |
<th align="left">Operator</th> |
|
147 |
<th align="left">Description</th> |
|
148 |
</tr> |
|
149 |
</thead> |
|
150 |
<tbody> |
|
151 |
<tr> |
|
152 |
<td align="left">==</td> |
|
153 |
<td align="left">left is equal to right (note that 1 is not equal to '1')</td> |
|
154 |
</tr> |
|
155 |
<tr> |
|
156 |
<td align="left">!=</td> |
|
157 |
<td align="left">left is not equal to right</td> |
|
158 |
</tr> |
|
159 |
<tr> |
|
160 |
<td align="left"><</td> |
|
161 |
<td align="left">left is less than right</td> |
|
162 |
</tr> |
|
163 |
<tr> |
|
164 |
<td align="left"><=</td> |
|
165 |
<td align="left">left is less or equal to right</td> |
|
166 |
</tr> |
|
167 |
<tr> |
|
168 |
<td align="left">></td> |
|
169 |
<td align="left">left is greater than right</td> |
|
170 |
</tr> |
|
171 |
<tr> |
|
172 |
<td align="left">>=</td> |
|
173 |
<td align="left">left is greater than or equal to right</td> |
|
174 |
</tr> |
|
175 |
<tr> |
|
176 |
<td align="left">=~</td> |
|
177 |
<td align="left">left matches regular expression [?(@.name =~ /foo.*?/i)]</td> |
|
178 |
</tr> |
|
179 |
<tr> |
|
180 |
<td align="left">in</td> |
|
181 |
<td align="left">left exists in right [?(@.size in ['S', 'M'])]</td> |
|
182 |
</tr> |
|
183 |
<tr> |
|
184 |
<td align="left">nin</td> |
|
185 |
<td align="left">left does not exists in right</td> |
|
186 |
</tr> |
|
187 |
<tr> |
|
188 |
<td align="left">subsetof</td> |
|
189 |
<td align="left">left is a subset of right [?(@.sizes subsetof ['S', 'M', 'L'])]</td> |
|
190 |
</tr> |
|
191 |
<tr> |
|
192 |
<td align="left">anyof</td> |
|
193 |
<td align="left">left has an intersection with right [?(@.sizes anyof ['M', 'L'])]</td> |
|
194 |
</tr> |
|
195 |
<tr> |
|
196 |
<td align="left">noneof</td> |
|
197 |
<td align="left">left has no intersection with right [?(@.sizes noneof ['M', 'L'])]</td> |
|
198 |
</tr> |
|
199 |
<tr> |
|
200 |
<td align="left">size</td> |
|
201 |
<td align="left">size of left (array or string) should match right</td> |
|
202 |
</tr> |
|
203 |
<tr> |
|
204 |
<td align="left">empty</td> |
|
205 |
<td align="left">left (array or string) should be empty</td> |
|
206 |
</tr> |
|
207 |
</tbody> |
|
208 |
</table> |
|
209 |
<h2>Path Examples</h2> |
|
210 |
<p>Given the json</p> |
|
211 |
<pre> |
|
212 |
{ |
|
213 |
"store": { |
|
214 |
"book": [ |
|
215 |
{ |
|
216 |
"category": "reference", |
|
217 |
"author": "Nigel Rees", |
|
218 |
"title": "Sayings of the Century", |
|
219 |
"price": 8.95 |
|
220 |
}, |
|
221 |
{ |
|
222 |
"category": "fiction", |
|
223 |
"author": "Evelyn Waugh", |
|
224 |
"title": "Sword of Honour", |
|
225 |
"price": 12.99 |
|
226 |
}, |
|
227 |
{ |
|
228 |
"category": "fiction", |
|
229 |
"author": "Herman Melville", |
|
230 |
"title": "Moby Dick", |
|
231 |
"isbn": "0-553-21311-3", |
|
232 |
"price": 8.99 |
|
233 |
}, |
|
234 |
{ |
|
235 |
"category": "fiction", |
|
236 |
"author": "J. R. R. Tolkien", |
|
237 |
"title": "The Lord of the Rings", |
|
238 |
"isbn": "0-395-19395-8", |
|
239 |
"price": 22.99 |
|
240 |
} |
|
241 |
], |
|
242 |
"bicycle": { |
|
243 |
"color": "red", |
|
244 |
"price": 19.95 |
|
245 |
} |
|
246 |
}, |
|
247 |
"expensive": 10 |
|
248 |
} |
|
249 |
</pre> |
|
250 |
|
|
251 |
<table> |
|
252 |
<thead> |
|
253 |
<tr> |
|
254 |
<th align="left">JsonPath</th> |
|
255 |
<th align="left">Result</th> |
|
256 |
</tr> |
|
257 |
</thead> |
|
258 |
<tbody> |
|
259 |
<tr> |
|
260 |
<td align="left">$.store.book[*].author</td> |
|
261 |
<td align="left">The authors of all books</td> |
|
262 |
</tr> |
|
263 |
<tr> |
|
264 |
<td align="left">$..author</td> |
|
265 |
<td align="left">All authors</td> |
|
266 |
</tr> |
|
267 |
<tr> |
|
268 |
<td align="left">$.store.*</td> |
|
269 |
<td align="left">All things, both books and bicycles</td> |
|
270 |
</tr> |
|
271 |
<tr> |
|
272 |
<td align="left">$.store..price</td> |
|
273 |
<td align="left">The price of everything</td> |
|
274 |
</tr> |
|
275 |
<tr> |
|
276 |
<td align="left">$..book[2]</td> |
|
277 |
<td align="left">The third book</td> |
|
278 |
</tr> |
|
279 |
<tr> |
|
280 |
<td align="left">$..book[-2]</td> |
|
281 |
<td align="left">The second to last book</td> |
|
282 |
</tr> |
|
283 |
<tr> |
|
284 |
<td align="left">$..book[0,1]</td> |
|
285 |
<td align="left">The first two books</td> |
|
286 |
</tr> |
|
287 |
<tr> |
|
288 |
<td align="left">$..book[:2]</td> |
|
289 |
<td align="left">All books from index 0 (inclusive) until index 2 (exclusive)</td> |
|
290 |
</tr> |
|
291 |
<tr> |
|
292 |
<td align="left">$..book[1:2]</td> |
|
293 |
<td align="left">All books from index 1 (inclusive) until index 2 (exclusive)</td> |
|
294 |
</tr> |
|
295 |
<tr> |
|
296 |
<td align="left">$..book[-2:]</td> |
|
297 |
<td align="left">Last two books</td> |
|
298 |
</tr> |
|
299 |
<tr> |
|
300 |
<td align="left">$..book[2:]</td> |
|
301 |
<td align="left">Book number two from tail</td> |
|
302 |
</tr> |
|
303 |
<tr> |
|
304 |
<td align="left">$..book[?(@.isbn)]</td> |
|
305 |
<td align="left">All books with an ISBN number</td> |
|
306 |
</tr> |
|
307 |
<tr> |
|
308 |
<td align="left">$.store.book[?(@.price< 10)]</td> |
|
309 |
<td align="left">All books in store cheaper than 10</td> |
|
310 |
</tr> |
|
311 |
<tr> |
|
312 |
<td align="left">$..book[?(@.price<= $['expensive'])]</td> |
|
313 |
<td align="left">All books in store that are not "expensive"</td> |
|
314 |
</tr> |
|
315 |
<tr> |
|
316 |
<td align="left">$..book[?(@.author=~ /.*REES/i)]</td> |
|
317 |
<td align="left">All books matching regex (ignore case)</td> |
|
318 |
</tr> |
|
319 |
<tr> |
|
320 |
<td align="left">$..*</td> |
|
321 |
<td align="left">Give me every thing</td> |
|
322 |
</tr> |
|
323 |
<tr> |
|
324 |
<td align="left">$..book.length()</td> |
|
325 |
<td align="left">The number of books</td> |
|
326 |
</tr> |
|
327 |
</tbody> |
|
328 |
</table> |
|
329 |
<br> |
|
330 |
<p>You can consult the documentation of the JSON_VALUE function of Oracle and SQL Server for more information.</p> |
trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.lib/org.gvsig.expressionevaluator.lib.impl/src/main/java/org/gvsig/expressionevaluator/impl/function/typeconversion/JsonValueFunction.java | ||
---|---|---|
22 | 22 |
*/ |
23 | 23 |
public class JsonValueFunction extends AbstractFunction { |
24 | 24 |
|
25 |
//JsonPathContextImpl implements JsonPathContext { |
|
26 |
// https://github.com/json-path/JsonPath |
|
25 |
// |
|
26 |
// function docs at: |
|
27 |
// Oracle json_value: https://docs.oracle.com/database/121/SQLRF/functions093.htm#SQLRF56668 |
|
28 |
// SQLServer json_value: https://docs.microsoft.com/es-es/sql/t-sql/functions/json-value-transact-sql?view=sql-server-ver15 |
|
29 |
// PostgreSQL jsonb_path_query: https://www.postgresql.org/docs/12/functions-json.html#FUNCTIONS-JSON-PROCESSING-TABLE |
|
30 |
// The implementation used is: |
|
31 |
// https://github.com/json-path/JsonPath |
|
32 |
// |
|
33 |
|
|
27 | 34 |
public JsonValueFunction() { |
28 | 35 |
super(GROUP_CONVERSION, FUNCTION_JSON_VALUE, Range.is(2), |
29 |
"Get value from a Json with a specific path.", |
|
30 |
FUNCTION_JSON_VALUE+"({{json}}, '[jsonpath]')"); |
|
31 |
this.addAlias("JSON_VALUE"); |
|
36 |
"finds a specified scalar JSON value in JSON data and returns it.", |
|
37 |
FUNCTION_JSON_VALUE+"({{expr}}, JsonPath)", |
|
38 |
new String[]{ |
|
39 |
"expr - specify an expression that evaluates to a text literal.", |
|
40 |
"JsonPath - specify a JSON path expression." |
|
41 |
} |
|
42 |
); |
|
32 | 43 |
} |
33 | 44 |
|
34 | 45 |
@Override |
... | ... | |
38 | 49 |
|
39 | 50 |
public static Object json_value(String json, String path) { |
40 | 51 |
JsonManager manager = ToolsUtilLocator.getJsonManager(); |
41 |
JsonPathContext context = manager.createJSonPathContext(json); |
|
52 |
JsonPathContext context; |
|
53 |
try { |
|
54 |
context = manager.createJSonPathContext(json); |
|
55 |
} catch(Exception ex) { |
|
56 |
return null; |
|
57 |
} |
|
42 | 58 |
Object value = context.get(path); |
43 | 59 |
if( value instanceof List ) { |
44 | 60 |
List l = ((List)value); |
... | ... | |
56 | 72 |
String path = this.getStr(args, 1); |
57 | 73 |
Object value = json_value(json, path); |
58 | 74 |
|
59 |
return value; } |
|
75 |
return value; |
|
76 |
} |
|
60 | 77 |
|
61 | 78 |
} |
trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.lib/org.gvsig.expressionevaluator.lib.impl/src/main/java/org/gvsig/expressionevaluator/impl/symboltable/UtilsSymbolTable.java | ||
---|---|---|
4 | 4 |
import org.gvsig.expressionevaluator.impl.function.numeric.DecrFunction; |
5 | 5 |
import org.gvsig.expressionevaluator.impl.function.numeric.IncrFunction; |
6 | 6 |
import org.gvsig.expressionevaluator.impl.function.programming.ColorFunction; |
7 |
import org.gvsig.expressionevaluator.impl.function.typeconversion.JsonValueFunction; |
|
7 | 8 |
import org.gvsig.expressionevaluator.spi.AbstractSymbolTable; |
8 | 9 |
|
9 | 10 |
/** |
... | ... | |
20 | 21 |
this.addFunction(new IncrFunction()); |
21 | 22 |
this.addFunction(new DecrFunction()); |
22 | 23 |
this.addFunction(new ColorFunction()); |
24 |
this.addFunction(new JsonValueFunction()); |
|
23 | 25 |
|
24 | 26 |
} |
25 | 27 |
|
trunk/org.gvsig.desktop/org.gvsig.desktop.library/org.gvsig.expressionevaluator/org.gvsig.expressionevaluator.swing/org.gvsig.expressionevaluator.swing.impl/src/main/java/org/gvsig/expressionevaluator/swing/impl/elements/DefaultFunctionElement.java | ||
---|---|---|
126 | 126 |
} |
127 | 127 |
if( !StringUtils.isBlank(functionDescription) ) { |
128 | 128 |
html.append("<b>").append(i18n.getTranslation("_Description")).append("</b><br>\n"); |
129 |
html.append(functionDescription.replace("\n", "<br>")).append("<br>\n"); |
|
129 |
if( !StringUtils.containsAny(functionDescription, "<br>", "<p>", "<br/>")) { |
|
130 |
html.append(functionDescription.replace("\n", "<br>")).append("<br>\n"); |
|
131 |
} else { |
|
132 |
html.append(functionDescription).append("<br>\n"); |
|
133 |
} |
|
130 | 134 |
} |
131 | 135 |
|
132 | 136 |
html.append("</html>\n"); |
Also available in: Unified diff