54
54
import java .util .Locale ;
55
55
import java .util .Map ;
56
56
import java .util .Map .Entry ;
57
+ import java .util .Objects ;
57
58
import java .util .Optional ;
58
59
import java .util .TreeMap ;
59
60
import java .util .function .IntConsumer ;
60
61
import java .util .logging .Level ;
61
62
import java .util .logging .Logger ;
63
+ import java .util .regex .MatchResult ;
62
64
import java .util .regex .Matcher ;
63
65
import java .util .regex .Pattern ;
64
66
import java .util .stream .IntStream ;
@@ -367,7 +369,7 @@ public static String breadcrumbPath(String urlPrefix, String path) {
367
369
* @param path the full path from which the breadcrumb path is built
368
370
* @param sep separator to use to crack the given path
369
371
*
370
- * @return HTML markup fro the breadcrumb or the path itself.
372
+ * @return HTML markup for the breadcrumb or the path itself.
371
373
* @see #breadcrumbPath(String, String, char, String, boolean, boolean)
372
374
*/
373
375
public static String breadcrumbPath (String urlPrefix , String path , char sep ) {
@@ -658,7 +660,7 @@ public static void encode(String s, Appendable dest) throws IOException {
658
660
// special html characters
659
661
dest .append ("&#" ).append ("" + (int ) c ).append (";" );
660
662
} else if (c == ' ' ) {
661
- // non breaking space
663
+ // non- breaking space
662
664
dest .append (" " );
663
665
} else if (c == '\t' ) {
664
666
dest .append (" " );
@@ -671,22 +673,6 @@ public static void encode(String s, Appendable dest) throws IOException {
671
673
}
672
674
}
673
675
674
- /**
675
- * Encode URL.
676
- *
677
- * @param urlStr string URL
678
- * @return the encoded URL
679
- * @throws URISyntaxException URI syntax
680
- * @throws MalformedURLException URL malformed
681
- */
682
- public static String encodeURL (String urlStr ) throws URISyntaxException , MalformedURLException {
683
- URL url = new URL (urlStr );
684
- URI constructed = new URI (url .getProtocol (), url .getUserInfo (),
685
- url .getHost (), url .getPort (),
686
- url .getPath (), url .getQuery (), url .getRef ());
687
- return constructed .toString ();
688
- }
689
-
690
676
/**
691
677
* Write out line information wrt. to the given annotation in the format:
692
678
* {@code Linenumber Blame Author} incl. appropriate links.
@@ -939,26 +925,22 @@ public static String uriEncode(String q) {
939
925
* @param dest a defined target
940
926
* @throws IOException I/O
941
927
*/
942
- public static void uriEncode (String str , Appendable dest )
943
- throws IOException {
928
+ public static void uriEncode (String str , Appendable dest ) throws IOException {
944
929
String uenc = uriEncode (str );
945
930
dest .append (uenc );
946
931
}
947
932
948
933
/**
949
- * Append '&name=value" to the given buffer. If the given
950
- * <var>value</var>
951
- * is {@code null}, this method does nothing.
934
+ * Append "&name=value" to the given buffer. If the given <var>value</var> is {@code null},
935
+ * this method does nothing.
952
936
*
953
937
* @param buf where to append the query string
954
938
* @param key the name of the parameter to add. Append as is!
955
- * @param value the value for the given parameter. Gets automatically UTF-8
956
- * URL encoded.
939
+ * @param value the value for the given parameter. Gets automatically UTF-8 URL encoded.
957
940
* @throws NullPointerException if the given buffer is {@code null}.
958
941
* @see #uriEncode(String)
959
942
*/
960
- public static void appendQuery (StringBuilder buf , String key ,
961
- String value ) {
943
+ public static void appendQuery (StringBuilder buf , String key , String value ) {
962
944
963
945
if (value != null ) {
964
946
buf .append (AMP ).append (key ).append ('=' ).append (uriEncode (value ));
@@ -1454,48 +1436,50 @@ private static String generatePageLink(int page, int offset, int limit, long siz
1454
1436
1455
1437
}
1456
1438
1457
-
1458
1439
/**
1459
- * Check if the string is a HTTP URL .
1440
+ * Check if the string is an HTTP(S) URI (i.e. allows for relative identifiers) .
1460
1441
*
1461
1442
* @param string the string to check
1462
- * @return true if it is http URL , false otherwise
1443
+ * @return true if it is HTTP(S) URI , false otherwise
1463
1444
*/
1464
1445
public static boolean isHttpUri (String string ) {
1465
- URL url ;
1446
+ URI uri ;
1466
1447
try {
1467
- url = new URL (string );
1468
- } catch (MalformedURLException ex ) {
1448
+ uri = new URI (string );
1449
+ } catch (URISyntaxException ex ) {
1469
1450
return false ;
1470
1451
}
1471
- return url .getProtocol ().equals ("http" ) || url .getProtocol ().equals ("https" );
1452
+ String scheme = uri .getScheme ();
1453
+ if (Objects .isNull (scheme )) {
1454
+ return false ;
1455
+ }
1456
+ return uri .getScheme ().equals ("http" ) || uri .getScheme ().equals ("https" );
1472
1457
}
1473
1458
1474
- protected static final String REDACTED_USER_INFO = "redacted_by_OpenGrok" ;
1459
+ static final String REDACTED_USER_INFO = "redacted_by_OpenGrok" ;
1475
1460
1476
1461
/**
1477
- * If given path is a URL , return the string representation with the user-info part filtered out.
1462
+ * If given path is a URI , return the string representation with the user-info part filtered out.
1478
1463
* @param path path to object
1479
- * @return either the original string or string representation of URL with the user-info part removed
1464
+ * @return either the original string (if the URI is not valid)
1465
+ * or string representation of the URI with the user-info part removed
1480
1466
*/
1481
- public static String redactUrl (String path ) {
1482
- URL url ;
1467
+ public static String redactUri (String path ) {
1468
+ URI uri ;
1483
1469
try {
1484
- url = new URL (path );
1485
- } catch (MalformedURLException e ) {
1486
- // not an URL
1470
+ uri = new URI (path );
1471
+ } catch (URISyntaxException e ) {
1487
1472
return path ;
1488
1473
}
1489
- if (url .getUserInfo () != null ) {
1490
- return url .toString ().replace (url .getUserInfo (),
1491
- REDACTED_USER_INFO );
1474
+ if (uri .getUserInfo () != null ) {
1475
+ return uri .toString ().replace (uri .getUserInfo (), REDACTED_USER_INFO );
1492
1476
} else {
1493
1477
return path ;
1494
1478
}
1495
1479
}
1496
1480
1497
1481
/**
1498
- * Build a HTML link to the given HTTP URL. If the URL is not an http URL
1482
+ * Build an HTML link to the given HTTP URL. If the URL is not an HTTP URL
1499
1483
* then it is returned as it was received. This has the same effect as
1500
1484
* invoking <code>linkify(url, true)</code>.
1501
1485
*
@@ -1509,7 +1493,7 @@ public static String linkify(String url) {
1509
1493
}
1510
1494
1511
1495
/**
1512
- * Build a html link to the given http URL. If the URL is not an http URL
1496
+ * Build an HTML link to the given HTTP URL. If the URL is not an HTTP URL
1513
1497
* then it is returned as it was received.
1514
1498
*
1515
1499
* @param url the HTTP URL
@@ -1535,9 +1519,9 @@ public static String linkify(String url, boolean newTab) {
1535
1519
}
1536
1520
1537
1521
/**
1538
- * Build an anchor with given name and a pack of attributes. Automatically
1539
- * escapes href attributes and automatically escapes the name into HTML
1540
- * entities.
1522
+ * Build an anchor with given name and a pack of attributes.
1523
+ * Assumes the <code> href</code> attribute value is URI encoded.
1524
+ * Automatically escapes the name into HTML entities.
1541
1525
*
1542
1526
* @param name displayed name of the anchor
1543
1527
* @param attrs map of attributes for the html element
@@ -1556,7 +1540,7 @@ public static String buildLink(String name, Map<String, String> attrs)
1556
1540
buffer .append ("=\" " );
1557
1541
String value = attr .getValue ();
1558
1542
if (attr .getKey ().equals ("href" )) {
1559
- value = Util . encodeURL (value );
1543
+ value = new URI (value ). toURL (). toString ( );
1560
1544
}
1561
1545
buffer .append (value );
1562
1546
buffer .append ("\" " );
@@ -1568,9 +1552,9 @@ public static String buildLink(String name, Map<String, String> attrs)
1568
1552
}
1569
1553
1570
1554
/**
1571
- * Build an anchor with given name and a pack of attributes. Automatically
1572
- * escapes href attributes and automatically escapes the name into HTML
1573
- * entities.
1555
+ * Build an anchor with given name and a pack of attributes.
1556
+ * Assumes the <code> href</code> attribute value is URI encoded.
1557
+ * Automatically escapes the name into HTML entities.
1574
1558
*
1575
1559
* @param name displayed name of the anchor
1576
1560
* @param url anchor's URL
@@ -1579,17 +1563,16 @@ public static String buildLink(String name, Map<String, String> attrs)
1579
1563
* @throws URISyntaxException URI syntax
1580
1564
* @throws MalformedURLException bad URL
1581
1565
*/
1582
- public static String buildLink (String name , String url )
1583
- throws URISyntaxException , MalformedURLException {
1566
+ public static String buildLink (String name , String url ) throws URISyntaxException , MalformedURLException {
1584
1567
Map <String , String > attrs = new TreeMap <>();
1585
1568
attrs .put ("href" , url );
1586
1569
return buildLink (name , attrs );
1587
1570
}
1588
1571
1589
1572
/**
1590
- * Build an anchor with given name and a pack of attributes. Automatically
1591
- * escapes href attributes and automatically escapes the name into HTML
1592
- * entities.
1573
+ * Build an anchor with given name and a pack of attributes.
1574
+ * Assumes the <code> href</code> attribute value is URI encoded.
1575
+ * Automatically escapes the name into HTML entities.
1593
1576
*
1594
1577
* @param name displayed name of the anchor
1595
1578
* @param url anchor's URL
@@ -1610,30 +1593,52 @@ public static String buildLink(String name, String url, boolean newTab)
1610
1593
return buildLink (name , attrs );
1611
1594
}
1612
1595
1596
+ /**
1597
+ * Callback function to provide replacement value for pattern match.
1598
+ * It replaces the first group of the pattern and retains the rest of the pattern.
1599
+ *
1600
+ * @param result {@link MatchResult} object representing pattern match
1601
+ * @param text original text containing the match
1602
+ * @param url URL to be used for the replacement
1603
+ * @return replacement for the first group in the pattern
1604
+ */
1605
+ private static String buildLinkReplacer (MatchResult result , String text , String url ) {
1606
+ if (result .groupCount () < 1 ) {
1607
+ return result .group (0 );
1608
+ }
1609
+ final String group1 = result .group (1 );
1610
+ final String appendedUrl = url + uriEncode (group1 );
1611
+ try {
1612
+ StringBuilder stringBuilder = new StringBuilder ();
1613
+ if (result .start (0 ) < result .start (1 )) {
1614
+ stringBuilder .append (text .substring (result .start (0 ), result .start (1 )));
1615
+ }
1616
+ stringBuilder .append (buildLink (group1 , appendedUrl , true ));
1617
+ if (result .end (1 ) < result .end (0 )) {
1618
+ stringBuilder .append (text .substring (result .end (1 ), result .end (0 )));
1619
+ }
1620
+ return stringBuilder .toString ();
1621
+ } catch (URISyntaxException | MalformedURLException e ) {
1622
+ LOGGER .log (Level .WARNING , "The given URL ''{0}'' is not valid" , appendedUrl );
1623
+ return result .group (0 );
1624
+ }
1625
+ }
1626
+
1613
1627
/**
1614
1628
* Replace all occurrences of pattern in the incoming text with the link
1615
- * named name pointing to an URL. It is possible to use the regexp pattern
1616
- * groups in name and URL when they are specified in the pattern.
1629
+ * named name pointing to a URL.
1617
1630
*
1618
- * @param text text to replace all patterns
1631
+ * @param text text to replace all patterns
1619
1632
* @param pattern the pattern to match
1620
- * @param name link display name
1621
- * @param url link URL
1633
+ * @param url link URL
1622
1634
* @return the text with replaced links
1623
1635
*/
1624
- public static String linkifyPattern (String text , Pattern pattern , String name , String url ) {
1625
- try {
1626
- String buildLink = buildLink (name , url , true );
1627
- return pattern .matcher (text ).replaceAll (buildLink );
1628
- } catch (URISyntaxException | MalformedURLException ex ) {
1629
- LOGGER .log (Level .WARNING , "The given URL ''{0}'' is not valid" , url );
1630
- return text ;
1631
- }
1636
+ public static String linkifyPattern (String text , Pattern pattern , String url ) {
1637
+ return pattern .matcher (text ).replaceAll (match -> buildLinkReplacer (match , text , url ));
1632
1638
}
1633
1639
1634
1640
/**
1635
- * Try to complete the given URL part into full URL with server name, port,
1636
- * scheme, ...
1641
+ * Try to complete the given URL part into full URL with server name, port, scheme, ...
1637
1642
* <dl>
1638
1643
* <dt>for request http://localhost:8080/source/xref/xxx and part
1639
1644
* /cgi-bin/user=</dt>
@@ -1655,7 +1660,8 @@ public static String completeUrl(String url, HttpServletRequest req) {
1655
1660
try {
1656
1661
if (!isHttpUri (url )) {
1657
1662
if (url .startsWith ("/" )) {
1658
- return new URI (req .getScheme (), null , req .getServerName (), req .getServerPort (), url , null , null ).toString ();
1663
+ return new URI (req .getScheme (), null , req .getServerName (), req .getServerPort (), url ,
1664
+ null , null ).toString ();
1659
1665
}
1660
1666
var prepUrl = req .getRequestURL ();
1661
1667
if (!url .isEmpty ()) {
@@ -1665,7 +1671,7 @@ public static String completeUrl(String url, HttpServletRequest req) {
1665
1671
}
1666
1672
return url ;
1667
1673
} catch (URISyntaxException ex ) {
1668
- LOGGER .log (Level .INFO ,
1674
+ LOGGER .log (Level .WARNING ,
1669
1675
String .format ("Unable to convert given URL part '%s' to complete URL" , url ),
1670
1676
ex );
1671
1677
return url ;
0 commit comments