1 package org.apache.velocity.tools.generic;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.UnsupportedEncodingException;
23 import java.net.URI;
24 import java.net.URLDecoder;
25 import java.net.URLEncoder;
26 import java.util.ArrayList;
27 import java.util.LinkedHashMap;
28 import java.util.List;
29 import java.util.Map;
30 import org.apache.velocity.runtime.log.Log;
31 import org.apache.velocity.tools.Scope;
32 import org.apache.velocity.tools.ToolContext;
33 import org.apache.velocity.tools.config.DefaultKey;
34 import org.apache.velocity.tools.config.SkipSetters;
35 import org.apache.velocity.tools.config.ValidScope;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 @DefaultKey("link")
80 @SkipSetters
81 @ValidScope(Scope.REQUEST)
82 public class LinkTool extends SafeConfig implements Cloneable
83 {
84
85 public static final String HTML_QUERY_DELIMITER = "&";
86
87
88 public static final String XHTML_QUERY_DELIMITER = "&";
89
90 public static final String APPEND_PARAMS_KEY = "appendParameters";
91 public static final String FORCE_RELATIVE_KEY = "forceRelative";
92 public static final String DEFAULT_CHARSET = "UTF-8";
93 public static final String DEFAULT_SCHEME = "http";
94 public static final String SECURE_SCHEME = "https";
95
96 public static final String URI_KEY = "uri";
97 public static final String SCHEME_KEY = "scheme";
98 public static final String USER_KEY = "user";
99 public static final String HOST_KEY = "host";
100 public static final String PORT_KEY = "port";
101 public static final String PATH_KEY = ToolContext.PATH_KEY;
102 public static final String QUERY_KEY = "params";
103 public static final String FRAGMENT_KEY = "anchor";
104 public static final String CHARSET_KEY = "charset";
105 public static final String XHTML_MODE_KEY = "xhtml";
106
107 protected Log LOG;
108 protected String scheme;
109 protected String user;
110 protected String host;
111 protected int port;
112 protected String path;
113 protected Map query;
114 protected String fragment;
115 protected String charset;
116 protected String queryDelim;
117 protected boolean appendParams;
118 protected boolean forceRelative;
119 protected boolean opaque;
120 protected final LinkTool self;
121
122
123
124
125
126 public LinkTool()
127 {
128 scheme = null;
129 user = null;
130 host = null;
131 port = -1;
132 path = null;
133 query = null;
134 fragment = null;
135 charset = DEFAULT_CHARSET;
136 queryDelim = XHTML_QUERY_DELIMITER;
137 opaque = false;
138 appendParams = true;
139 forceRelative = false;
140 self = this;
141 }
142
143 protected final void debug(String msg, Object... args)
144 {
145 debug(msg, null, args);
146 }
147
148 protected final void debug(String msg, Throwable t, Object... args)
149 {
150 if (LOG != null && LOG.isDebugEnabled())
151 {
152 LOG.debug("LinkTool: "+String.format(msg, args), t);
153 }
154 }
155
156
157
158
159 protected void configure(ValueParser props)
160 {
161 this.LOG = (Log)props.getValue(ToolContext.LOG_KEY);
162
163 String link = props.getString(URI_KEY);
164 if (link != null)
165 {
166 setFromURI(link);
167 }
168
169 String schm = props.getString(SCHEME_KEY);
170 if (schm != null)
171 {
172 setScheme(schm);
173 }
174 String info = props.getString(USER_KEY);
175 if (info != null)
176 {
177 setUserInfo(info);
178 }
179 String hst = props.getString(HOST_KEY);
180 if (hst != null)
181 {
182 setHost(hst);
183 }
184 Integer prt = props.getInteger(PORT_KEY);
185 if (prt != null)
186 {
187 setPort(prt.intValue());
188 }
189 String pth = props.getString(PATH_KEY);
190 if (pth != null)
191 {
192 setPath(pth);
193 }
194 String params = props.getString(QUERY_KEY);
195 if (params != null)
196 {
197 setQuery(params);
198 }
199 String anchor = props.getString(FRAGMENT_KEY);
200 if (anchor != null)
201 {
202 setFragment(anchor);
203 }
204
205 String chrst = props.getString(CHARSET_KEY);
206 if (chrst != null)
207 {
208 setCharacterEncoding(chrst);
209 }
210
211 Boolean xhtml = props.getBoolean(XHTML_MODE_KEY);
212 if (xhtml != null)
213 {
214 setXHTML(xhtml);
215 }
216 Boolean addParams = props.getBoolean(APPEND_PARAMS_KEY);
217 if (addParams != null)
218 {
219 setAppendParams(addParams);
220 }
221 Boolean forceRelative = props.getBoolean(FORCE_RELATIVE_KEY);
222 if (forceRelative != null)
223 {
224 setForceRelative(forceRelative);
225 }
226 }
227
228
229
230
231
232
233 protected LinkTool duplicate()
234 {
235 return duplicate(false);
236 }
237
238
239
240
241
242
243
244 protected LinkTool duplicate(boolean deep)
245 {
246 try
247 {
248 LinkTool that = (LinkTool)this.clone();
249 if (deep && query != null)
250 {
251 that.query = new LinkedHashMap(query);
252 }
253 return that;
254 }
255 catch (CloneNotSupportedException e)
256 {
257 String msg = "Could not properly clone " + getClass();
258 if (LOG != null)
259 {
260 LOG.error(msg, e);
261 }
262 throw new RuntimeException(msg, e);
263 }
264 }
265
266 public void setCharacterEncoding(String chrst)
267 {
268 this.charset = chrst;
269 }
270
271
272
273
274
275
276
277
278
279
280
281
282
283 public void setXHTML(boolean xhtml)
284 {
285 queryDelim = (xhtml) ? XHTML_QUERY_DELIMITER : HTML_QUERY_DELIMITER;
286 }
287
288
289
290
291
292
293 public void setAppendParams(boolean addParams)
294 {
295 this.appendParams = addParams;
296 }
297
298
299
300
301
302
303
304
305
306 public void setForceRelative(boolean forceRelative)
307 {
308 this.forceRelative = forceRelative;
309 }
310
311
312
313
314
315 public void setScheme(Object obj)
316 {
317 if (obj == null)
318 {
319 this.scheme = null;
320 }
321 else
322 {
323 this.scheme = String.valueOf(obj);
324 if (scheme.length() == 0)
325 {
326 this.scheme = null;
327 }
328 if (scheme.endsWith(":"))
329 {
330 this.scheme = scheme.substring(0, scheme.length() - 1);
331 }
332 }
333 }
334
335 public void setUserInfo(Object obj)
336 {
337 this.user = obj == null ? null : String.valueOf(obj);
338 }
339
340 public void setHost(Object obj)
341 {
342 this.host = obj == null ? null : String.valueOf(obj);
343 }
344
345
346
347
348
349
350 public void setPort(Object obj)
351 {
352 if (obj == null)
353 {
354 this.port = -1;
355 }
356 else if (obj instanceof Number)
357 {
358 this.port = ((Number)obj).intValue();
359 }
360 else
361 {
362 try
363 {
364 this.port = Integer.parseInt(String.valueOf(obj));
365 }
366 catch (NumberFormatException nfe)
367 {
368 debug("Could convert '%s' to int", nfe, obj);
369 this.port = -2;
370 }
371 }
372 }
373
374
375
376
377
378
379 public void setPath(Object obj)
380 {
381 if (obj == null)
382 {
383 this.path = null;
384 }
385 else
386 {
387 this.path = String.valueOf(obj);
388 if (!this.opaque && !path.startsWith("/"))
389 {
390 this.path = '/' + this.path;
391 }
392 }
393 }
394
395
396
397
398
399
400
401 public void appendPath(Object obj)
402 {
403 if (obj != null && !this.opaque)
404 {
405 setPath(combinePath(getPath(), String.valueOf(obj)));
406 }
407 }
408
409
410
411
412
413
414
415 protected String combinePath(String start, String end)
416 {
417 if (end == null)
418 {
419 return start;
420 }
421 if (start == null)
422 {
423 return end;
424 }
425
426
427 boolean startEnds = start.endsWith("/");
428 boolean endStarts = end.startsWith("/");
429 if (startEnds ^ endStarts)
430 {
431 return start + end;
432 }
433 else if (startEnds & endStarts)
434 {
435 return start + end.substring(1, end.length());
436 }
437 else
438 {
439 return start + '/' + end;
440 }
441 }
442
443
444
445
446
447
448
449
450 public void setQuery(Object obj)
451 {
452 if (obj == null)
453 {
454 this.query = null;
455 }
456 else if (obj instanceof Map)
457 {
458 this.query = new LinkedHashMap((Map)obj);
459 }
460 else
461 {
462 String qs = normalizeQuery(String.valueOf(obj));
463 this.query = parseQuery(qs);
464 }
465 }
466
467 protected String normalizeQuery(String qs)
468 {
469
470 if (qs.indexOf('&') >= 0)
471 {
472
473
474 qs = qs.replaceAll("&(amp;)?", queryDelim);
475 }
476 return qs;
477 }
478
479
480
481
482 public String toQuery(Map parameters)
483 {
484 if (parameters == null)
485 {
486 return null;
487 }
488 StringBuilder query = new StringBuilder();
489 for (Object e : parameters.entrySet())
490 {
491 Map.Entry entry = (Map.Entry)e;
492
493 if (query.length() > 0)
494 {
495 query.append(queryDelim);
496 }
497 query.append(toQuery(entry.getKey(), entry.getValue()));
498 }
499 return query.toString();
500 }
501
502
503
504
505
506 public void appendQuery(Object obj)
507 {
508 if (obj != null)
509 {
510 setQuery(combineQuery(getQuery(), String.valueOf(obj)));
511 }
512 }
513
514
515
516
517
518
519
520
521
522
523 public void setParam(Object key, Object value, boolean append)
524 {
525
526 key = String.valueOf(key);
527 if (this.query == null)
528 {
529 this.query = new LinkedHashMap();
530 putParam(key, value);
531 }
532 else if (append)
533 {
534 appendParam((String)key, value);
535 }
536 else
537 {
538 putParam(key, value);
539 }
540 }
541
542 private void appendParam(String key, Object value)
543 {
544 if (query.containsKey(key))
545 {
546 Object cur = query.get(key);
547 if (cur instanceof List)
548 {
549 addToList((List)cur, value);
550 }
551 else
552 {
553 List vals = new ArrayList();
554 vals.add(cur);
555 addToList(vals, value);
556 putParam(key, vals);
557 }
558 }
559 else
560 {
561 putParam(key, value);
562 }
563 }
564
565 private void putParam(Object key, Object value)
566 {
567 if (value instanceof Object[])
568 {
569 List vals = new ArrayList();
570 for (Object v : ((Object[])value))
571 {
572 vals.add(v);
573 }
574 value = vals;
575 }
576 query.put(key, value);
577 }
578
579 private void addToList(List vals, Object value)
580 {
581 if (value instanceof List)
582 {
583 for (Object v : ((List)value))
584 {
585 vals.add(v);
586 }
587 }
588 else if (value instanceof Object[])
589 {
590 for (Object v : ((Object[])value))
591 {
592 vals.add(v);
593 }
594 }
595 else
596 {
597 vals.add(value);
598 }
599 }
600
601
602
603
604
605
606
607
608 public void setParams(Object obj, boolean append)
609 {
610 if (!append)
611 {
612 setQuery(obj);
613 }
614 else if (obj != null)
615 {
616 if (!(obj instanceof Map))
617 {
618 obj = parseQuery(String.valueOf(obj));
619 }
620 if (obj != null)
621 {
622 if (query == null)
623 {
624 this.query = new LinkedHashMap();
625 }
626 for (Object e : ((Map)obj).entrySet())
627 {
628 Map.Entry entry = (Map.Entry)e;
629 String key = String.valueOf(entry.getKey());
630 appendParam(key, entry.getValue());
631 }
632 }
633 }
634 }
635
636
637
638
639
640 public Object removeParam(Object key)
641 {
642 if (query != null)
643 {
644 key = String.valueOf(key);
645 return query.remove(key);
646 }
647 return null;
648 }
649
650
651
652
653
654 protected void handleParamsBoolean(boolean keep)
655 {
656 if (!keep)
657 {
658 setQuery(null);
659 }
660 }
661
662
663
664
665
666
667
668
669 protected String combineQuery(String current, String add)
670 {
671 if (add == null || add.length() == 0)
672 {
673 return current;
674 }
675 if (add.startsWith("?"))
676 {
677 add = add.substring(1, add.length());
678 }
679 if (current == null || current.length() == 0)
680 {
681 return add;
682 }
683 if (current.endsWith(queryDelim))
684 {
685 current = current.substring(0, current.length() - queryDelim.length());
686 }
687 else if (current.endsWith("&"))
688 {
689 current = current.substring(0, current.length() - 1);
690 }
691 if (add.startsWith(queryDelim))
692 {
693 return current + add;
694 }
695 else if (add.startsWith("&"))
696 {
697
698 add = add.substring(1, add.length());
699 }
700 return current + queryDelim + add;
701 }
702
703
704
705
706
707
708
709 protected String toQuery(Object key, Object value)
710 {
711 StringBuilder out = new StringBuilder();
712 if (value == null)
713 {
714 out.append(encode(key));
715 out.append('=');
716
717 }
718 else if (value instanceof List)
719 {
720 appendAsArray(out, key, ((List)value).toArray());
721 }
722 else if (value instanceof Object[])
723 {
724 appendAsArray(out, key, (Object[])value);
725 }
726 else
727 {
728 out.append(encode(key));
729 out.append('=');
730 out.append(encode(value));
731 }
732 return out.toString();
733 }
734
735
736 protected void appendAsArray(StringBuilder out, Object key, Object[] arr)
737 {
738 String encKey = encode(key);
739 for (int i=0; i < arr.length; i++)
740 {
741 out.append(encKey);
742 out.append('=');
743 if (arr[i] != null)
744 {
745 out.append(encode(arr[i]));
746 }
747 if (i+1 < arr.length)
748 {
749 out.append(queryDelim);
750 }
751 }
752 }
753
754
755
756
757
758
759
760 protected Map<String,Object> parseQuery(String query)
761 {
762 return parseQuery(normalizeQuery(query), this.queryDelim);
763 }
764
765
766
767
768
769
770
771
772 protected Map<String,Object> parseQuery(String query, String queryDelim)
773 {
774 if (query.startsWith("?"))
775 {
776 query = query.substring(1, query.length());
777 }
778 String[] pairs = query.split(queryDelim);
779 if (pairs.length == 0)
780 {
781 return null;
782 }
783 Map<String,Object> params = new LinkedHashMap<String,Object>(pairs.length);
784 for (String pair : pairs)
785 {
786 String[] kv = pair.split("=");
787 String key = kv[0];
788 Object value = kv.length > 1 ? kv[1] : null;
789 if (params.containsKey(kv[0]))
790 {
791 Object oldval = params.get(key);
792 if (oldval instanceof List)
793 {
794 ((List)oldval).add((String)value);
795 value = oldval;
796 }
797 else
798 {
799 List<String> list = new ArrayList<String>();
800 list.add((String)oldval);
801 list.add((String)value);
802 value = list;
803 }
804 }
805 params.put(key, value);
806 }
807 return params;
808 }
809
810
811
812
813 public void setFragment(Object obj)
814 {
815 if (obj == null)
816 {
817 this.fragment = null;
818 }
819 else
820 {
821 this.fragment = String.valueOf(obj);
822 if (this.fragment.length() == 0)
823 {
824 this.fragment = null;
825 }
826 }
827 }
828
829
830
831
832
833
834
835
836 protected boolean setFromURI(Object obj)
837 {
838 if (obj == null)
839 {
840
841 setScheme(null);
842 setUserInfo(null);
843 setHost(null);
844 setPort(null);
845 setPath(null);
846 setQuery(null);
847 setFragment(null);
848 return true;
849 }
850
851 URI uri = toURI(obj);
852 if (uri == null)
853 {
854 return false;
855 }
856 setScheme(uri.getScheme());
857 if (uri.isOpaque())
858 {
859 this.opaque = true;
860
861 setPath(uri.getSchemeSpecificPart());
862 }
863 else
864 {
865 setUserInfo(uri.getUserInfo());
866 setHost(uri.getHost());
867 setPort(uri.getPort());
868 String pth = uri.getPath();
869 if (pth.equals("/") || pth.length() == 0)
870 {
871 pth = null;
872 }
873 setPath(pth);
874 setQuery(uri.getQuery());
875 }
876 setFragment(uri.getFragment());
877 return true;
878 }
879
880
881
882
883 protected URI toURI(Object obj)
884 {
885 if (obj instanceof URI)
886 {
887 return (URI)obj;
888 }
889 else
890 {
891 try
892 {
893 return new URI(String.valueOf(obj));
894 }
895 catch (Exception e)
896 {
897 debug("Could convert '%s' to URI", e, obj);
898 return null;
899 }
900 }
901 }
902
903
904
905
906
907
908 protected URI createURI()
909 {
910 try
911 {
912
913 if (port > -2)
914 {
915 if (opaque)
916 {
917
918 return new URI(scheme, path, fragment);
919 }
920 else if (forceRelative)
921 {
922 if (path == null && query == null && fragment == null)
923 {
924 return null;
925 }
926 return new URI(null, null, null, -1, path, toQuery(query), fragment);
927 }
928 else
929 {
930
931 if (scheme == null && user == null && host == null
932 && path == null && query == null && fragment == null)
933 {
934 return null;
935 }
936 return new URI(scheme, user, host, port, path, toQuery(query), fragment);
937 }
938 }
939 }
940 catch (Exception e)
941 {
942 debug("Could not create URI", e);
943 }
944 return null;
945 }
946
947
948
949
950
951
952
953 public String getCharacterEncoding()
954 {
955 return this.charset;
956 }
957
958
959
960
961
962
963 public boolean isXHTML()
964 {
965 return queryDelim.equals(XHTML_QUERY_DELIMITER);
966 }
967
968
969
970
971
972 public boolean getAppendParams()
973 {
974 return this.appendParams;
975 }
976
977
978
979
980
981 public LinkTool scheme(Object scheme)
982 {
983 LinkTool copy = duplicate();
984 copy.setScheme(scheme);
985 return copy;
986 }
987
988
989
990
991 public LinkTool secure()
992 {
993 return scheme(SECURE_SCHEME);
994 }
995
996
997
998
999 public LinkTool insecure()
1000 {
1001 return scheme(DEFAULT_SCHEME);
1002 }
1003
1004
1005
1006
1007 public String getScheme()
1008 {
1009 return scheme;
1010 }
1011
1012
1013
1014
1015 public boolean isSecure()
1016 {
1017 return SECURE_SCHEME.equalsIgnoreCase(getScheme());
1018 }
1019
1020
1021
1022
1023
1024 public boolean isOpaque()
1025 {
1026 return this.opaque;
1027 }
1028
1029
1030
1031
1032
1033 public LinkTool user(Object info)
1034 {
1035 LinkTool copy = duplicate();
1036 copy.setUserInfo(info);
1037 return copy;
1038 }
1039
1040
1041
1042
1043 public String getUser()
1044 {
1045 return this.user;
1046 }
1047
1048
1049
1050
1051
1052
1053 public LinkTool host(Object host)
1054 {
1055 LinkTool copy = duplicate();
1056 copy.setHost(host);
1057
1058 if (copy.getHost() != null && !copy.isAbsolute())
1059 {
1060
1061 copy.setScheme(DEFAULT_SCHEME);
1062 }
1063 return copy;
1064 }
1065
1066
1067
1068
1069 public String getHost()
1070 {
1071 return this.host;
1072 }
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082 public LinkTool port(Object port)
1083 {
1084 LinkTool copy = duplicate();
1085 copy.setPort(port);
1086 return copy;
1087 }
1088
1089
1090
1091
1092 public Integer getPort()
1093 {
1094 if (this.port < 0)
1095 {
1096 return null;
1097 }
1098 return this.port;
1099 }
1100
1101
1102
1103
1104
1105 public LinkTool path(Object pth)
1106 {
1107 LinkTool copy = duplicate();
1108 copy.setPath(pth);
1109 return copy;
1110 }
1111
1112
1113
1114
1115 public String getPath()
1116 {
1117 return this.path;
1118 }
1119
1120
1121
1122
1123
1124 public LinkTool append(Object pth)
1125 {
1126 LinkTool copy = duplicate();
1127 copy.appendPath(pth);
1128 return copy;
1129 }
1130
1131
1132
1133
1134
1135
1136 public String getDirectory()
1137 {
1138 if (this.path == null || this.opaque)
1139 {
1140 return null;
1141 }
1142 int lastSlash = this.path.lastIndexOf('/');
1143 if (lastSlash < 0)
1144 {
1145 return "";
1146 }
1147 return this.path.substring(0, lastSlash + 1);
1148 }
1149
1150
1151
1152
1153
1154 public String getFile()
1155 {
1156 if (this.path == null || this.opaque)
1157 {
1158 return null;
1159 }
1160 int lastSlash = this.path.lastIndexOf('/');
1161 if (lastSlash < 0)
1162 {
1163 return this.path;
1164 }
1165 return this.path.substring(lastSlash + 1, this.path.length());
1166 }
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177 public String getRoot()
1178 {
1179 LinkTool root = root();
1180 if (root == null)
1181 {
1182 return null;
1183 }
1184 return root.toString();
1185 }
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195 public LinkTool root()
1196 {
1197 if (host == null || opaque || port == -2)
1198 {
1199 return null;
1200 }
1201 LinkTool copy = absolute();
1202 copy.setPath(null);
1203 copy.setQuery(null);
1204 copy.setFragment(null);
1205 return copy;
1206 }
1207
1208
1209
1210
1211
1212
1213 public LinkTool directory()
1214 {
1215 LinkTool copy = root();
1216 if (copy == null)
1217 {
1218 copy = duplicate();
1219
1220 copy.setQuery(null);
1221 copy.setFragment(null);
1222 }
1223 copy.setPath(getDirectory());
1224 return copy;
1225 }
1226
1227
1228
1229
1230
1231 public boolean isRelative()
1232 {
1233 return (this.forceRelative || this.scheme == null);
1234 }
1235
1236
1237
1238
1239
1240 public LinkTool relative()
1241 {
1242 LinkTool copy = duplicate();
1243 copy.setForceRelative(true);
1244 return copy;
1245 }
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262 public LinkTool relative(Object obj)
1263 {
1264 LinkTool copy = relative();
1265
1266 String pth;
1267 if (obj == null)
1268 {
1269 pth = getContextPath();
1270 }
1271 else
1272 {
1273 pth = combinePath(getContextPath(), String.valueOf(obj));
1274 }
1275 copy.setPath(pth);
1276 return copy;
1277 }
1278
1279
1280
1281
1282
1283
1284
1285
1286 public String getContextPath()
1287 {
1288 return getDirectory();
1289 }
1290
1291
1292
1293
1294
1295 public boolean isAbsolute()
1296 {
1297 return (this.scheme != null && !this.forceRelative);
1298 }
1299
1300
1301
1302
1303
1304
1305 public LinkTool absolute()
1306 {
1307 LinkTool copy = duplicate();
1308 copy.setForceRelative(false);
1309 if (copy.getScheme() == null)
1310 {
1311 copy.setScheme(DEFAULT_SCHEME);
1312 }
1313 return copy;
1314 }
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339 public LinkTool absolute(Object obj)
1340 {
1341
1342 LinkTool copy = absolute();
1343 String pth;
1344 if (obj == null)
1345 {
1346
1347 pth = getDirectory();
1348 }
1349 else
1350 {
1351 pth = String.valueOf(obj);
1352 if (pth.startsWith(DEFAULT_SCHEME))
1353 {
1354
1355 URI uri = toURI(pth);
1356 if (uri == null)
1357 {
1358 return null;
1359 }
1360 copy.setScheme(uri.getScheme());
1361 copy.setUserInfo(uri.getUserInfo());
1362 copy.setHost(uri.getHost());
1363 copy.setPort(uri.getPort());
1364
1365 pth = uri.getPath();
1366 if (pth.equals("/") || pth.length() == 0)
1367 {
1368 pth = null;
1369 }
1370 copy.setPath(pth);
1371 if (uri.getQuery() != null)
1372 {
1373 copy.setQuery(uri.getQuery());
1374 }
1375 if (uri.getFragment() != null)
1376 {
1377 copy.setFragment(uri.getFragment());
1378 }
1379 return copy;
1380 }
1381 else if (!pth.startsWith("/"))
1382 {
1383
1384
1385 pth = combinePath(getDirectory(), pth);
1386 }
1387 }
1388 copy.setPath(pth);
1389 return copy;
1390 }
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402 public LinkTool uri(Object uri)
1403 {
1404 LinkTool copy = duplicate();
1405 if (copy.setFromURI(uri))
1406 {
1407 return copy;
1408 }
1409 return null;
1410 }
1411
1412
1413
1414
1415
1416
1417
1418 public URI getUri()
1419 {
1420 if (!isSafeMode())
1421 {
1422 return createURI();
1423 }
1424 return null;
1425 }
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435 public String getBaseRef()
1436 {
1437 LinkTool copy = duplicate();
1438 copy.setQuery(null);
1439 copy.setFragment(null);
1440 return copy.toString();
1441 }
1442
1443
1444
1445
1446
1447
1448 public LinkTool query(Object query)
1449 {
1450 LinkTool copy = duplicate();
1451 copy.setQuery(query);
1452 return copy;
1453 }
1454
1455
1456
1457
1458 public String getQuery()
1459 {
1460 return toQuery(this.query);
1461 }
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474 public LinkTool param(Object key, Object value)
1475 {
1476 LinkTool copy = duplicate(true);
1477 copy.setParam(key, value, this.appendParams);
1478 return copy;
1479 }
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489 public LinkTool append(Object key, Object value)
1490 {
1491 LinkTool copy = duplicate(true);
1492 copy.setParam(key, value, true);
1493 return copy;
1494 }
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505 public LinkTool set(Object key, Object value)
1506 {
1507 LinkTool copy = duplicate(true);
1508 copy.setParam(key, value, false);
1509 return copy;
1510 }
1511
1512
1513
1514
1515
1516
1517
1518
1519 public LinkTool remove(Object key)
1520 {
1521 LinkTool copy = duplicate(true);
1522 copy.removeParam(key);
1523 return copy;
1524 }
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540 public LinkTool params(Object parameters)
1541 {
1542
1543 if (parameters == null)
1544 {
1545 return this;
1546 }
1547 if (parameters instanceof Boolean)
1548 {
1549 Boolean action = ((Boolean)parameters).booleanValue();
1550 LinkTool copy = duplicate(true);
1551 copy.handleParamsBoolean(action);
1552 return copy;
1553 }
1554 if (parameters instanceof Map && ((Map)parameters).isEmpty())
1555 {
1556 return duplicate(false);
1557 }
1558
1559 LinkTool copy = duplicate(this.appendParams);
1560 copy.setParams(parameters, this.appendParams);
1561 return copy;
1562 }
1563
1564 public Map getParams()
1565 {
1566 if (this.query == null || this.query.isEmpty())
1567 {
1568 return null;
1569 }
1570 return this.query;
1571 }
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585 public LinkTool anchor(Object anchor)
1586 {
1587 LinkTool copy = duplicate();
1588 copy.setFragment(anchor);
1589 return copy;
1590 }
1591
1592
1593
1594
1595 public String getAnchor()
1596 {
1597 return this.fragment;
1598 }
1599
1600 public LinkTool getSelf()
1601 {
1602
1603 return self;
1604 }
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614 public String toString()
1615 {
1616 URI uri = createURI();
1617 if (uri == null)
1618 {
1619 return null;
1620 }
1621 if (query != null)
1622 {
1623 return decodeQueryPercents(uri.toString());
1624 }
1625 return uri.toString();
1626 }
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637 protected String decodeQueryPercents(String url)
1638 {
1639 StringBuilder out = new StringBuilder(url.length());
1640 boolean inQuery = false, havePercent = false, haveTwo = false;
1641 for (int i=0; i<url.length(); i++)
1642 {
1643 char c = url.charAt(i);
1644 if (inQuery)
1645 {
1646 if (havePercent)
1647 {
1648 if (haveTwo)
1649 {
1650 out.append('%');
1651 if (c != '5')
1652 {
1653 out.append('2').append(c);
1654 }
1655 havePercent = haveTwo = false;
1656 }
1657 else if (c == '2')
1658 {
1659 haveTwo = true;
1660 }
1661 else
1662 {
1663 out.append('%').append(c);
1664 havePercent = false;
1665 }
1666 }
1667 else if (c == '%')
1668 {
1669 havePercent = true;
1670 }
1671 else
1672 {
1673 out.append(c);
1674 }
1675 if (c == '#')
1676 {
1677 inQuery = false;
1678 }
1679 }
1680 else
1681 {
1682 out.append(c);
1683 if (c == '?')
1684 {
1685 inQuery = true;
1686 }
1687 }
1688 }
1689
1690 if (havePercent)
1691 {
1692 out.append('%');
1693 if (haveTwo)
1694 {
1695 out.append('2');
1696 }
1697 }
1698 return out.toString();
1699 }
1700
1701
1702
1703
1704
1705
1706
1707 @Override
1708 public boolean equals(Object obj)
1709 {
1710 if (obj == null || !(obj instanceof LinkTool))
1711 {
1712 return false;
1713 }
1714
1715 String that = obj.toString();
1716 if (that == null && toString() == null)
1717 {
1718 return true;
1719 }
1720 return that.equals(toString());
1721 }
1722
1723
1724
1725
1726
1727
1728 @Override
1729 public int hashCode()
1730 {
1731 String hashme = toString();
1732 if (hashme == null)
1733 {
1734 return -1;
1735 }
1736 return hashme.hashCode();
1737 }
1738
1739
1740
1741
1742
1743
1744
1745
1746 public String encode(Object obj)
1747 {
1748 if (obj == null)
1749 {
1750 return null;
1751 }
1752 try
1753 {
1754 return URLEncoder.encode(String.valueOf(obj), charset);
1755 }
1756 catch (UnsupportedEncodingException uee)
1757 {
1758 debug("Character encoding '%s' is unsupported", uee, charset);
1759 return null;
1760 }
1761 }
1762
1763
1764
1765
1766
1767
1768
1769 public String decode(Object obj)
1770 {
1771 if (obj == null)
1772 {
1773 return null;
1774 }
1775 try
1776 {
1777 return URLDecoder.decode(String.valueOf(obj), charset);
1778 }
1779 catch (UnsupportedEncodingException uee)
1780 {
1781 debug("Character encoding '%s' is unsupported", uee, charset);
1782 return null;
1783 }
1784 }
1785
1786 }