39
39
import java .nio .charset .StandardCharsets ;
40
40
import java .security .MessageDigest ;
41
41
import java .security .NoSuchAlgorithmException ;
42
- import java .text .SimpleDateFormat ;
42
+ import java .time .ZoneId ;
43
+ import java .time .ZonedDateTime ;
44
+ import java .time .format .DateTimeFormatter ;
45
+ import java .time .format .FormatStyle ;
46
+ import java .util .ArrayList ;
47
+ import java .util .Comparator ;
43
48
import java .util .Date ;
49
+ import java .util .List ;
44
50
import java .util .Locale ;
45
51
import java .util .Map ;
46
52
@@ -109,32 +115,72 @@ public static StringBuilder renderDirectoryListing(final HttpServerExchange exch
109
115
path += "/" ;
110
116
}
111
117
112
- String relative = null ;
118
+ String relative = determineRelativePath (exchange , path );
119
+
120
+ String sortColumn = "name" ;
121
+ String currentSortOrder = "asc" ;
122
+
113
123
if (exchange != null ) {
114
- final Map <String , Object > context = exchange .getAttachment (Predicate .PREDICATE_CONTEXT );
115
- if (context != null ) {
116
- final PathPrefixPredicate .PathPrefixMatchRecord trans = (PathPrefixMatchRecord ) context
117
- .get (PathPrefixPredicate .PREFIX_MATCH_RECORD );
118
- if (trans != null ) {
119
- if (trans .isOverWritten ()) {
120
- relative = trans .getPrefix ();
121
- if (!relative .endsWith ("/" ) && !path .startsWith ("/" )) {
122
- relative += "/" ;
123
- }
124
- }
125
- }
124
+ if (exchange .getQueryParameters ().get ("sort" ) != null ) {
125
+ sortColumn = exchange .getQueryParameters ().get ("sort" ).getFirst ();
126
+ }
127
+ if (exchange .getQueryParameters ().get ("order" ) != null ) {
128
+ currentSortOrder = exchange .getQueryParameters ().get ("order" ).getFirst ();
126
129
}
127
130
}
128
131
129
- StringBuilder builder = new StringBuilder ();
130
- builder .append ("<html>\n <head>\n <script src='" ).append (relative == null ? path : relative + path ).append ("?js'></script>\n " )
131
- .append ("<link rel='stylesheet' type='text/css' href='" ).append (relative == null ? path : relative + path ).append ("?css' />\n </head>\n " );
132
- builder .append ("<body onresize='growit()' onload='growit()'>\n <table id='thetable'>\n <thead>\n " );
133
- builder .append ("<tr><th class='loc' colspan='3'>Directory Listing - " ).append (relative == null ? path : relative + path ).append ("</th></tr>\n " )
134
- .append ("<tr><th class='label offset'>Name</th><th class='label'>Last Modified</th><th class='label'>Size</th></tr>\n </thead>\n " )
135
- .append ("<tfoot>\n <tr><th class=\" loc footer\" colspan=\" 3\" >Powered by Undertow</th></tr>\n </tfoot>\n <tbody>\n " );
132
+ String newSortOrder = "asc" .equals (currentSortOrder ) ? "desc" : "asc" ;
133
+ String sortUrl = relative == null ? path : relative + path ;
134
+
135
+ StringBuilder builder = buildDirectoryListingTable (sortUrl , sortColumn , newSortOrder );
136
136
137
137
int state = 0 ;
138
+ String parent = getParentPath (path , state );
139
+
140
+ int i = 0 ;
141
+ if (parent != null ) {
142
+ i ++;
143
+ appendParentDirectory (resource , builder , relative , parent );
144
+ }
145
+
146
+ List <Resource > directories = new ArrayList <>();
147
+ List <Resource > files = new ArrayList <>();
148
+ separateDirectoriesAndFiles (resource , directories , files );
149
+
150
+ Comparator <Resource > comparator = getComparator (sortColumn , currentSortOrder );
151
+ directories .sort (comparator );
152
+ files .sort (comparator );
153
+
154
+ appendDirectories (directories , builder , i , sortUrl );
155
+ appendFiles (files , builder , i , sortUrl );
156
+
157
+ builder .append ("</tbody>\n </table>\n </body>\n </html>" );
158
+
159
+ return builder ;
160
+
161
+ }
162
+
163
+ private static String formatLastModified (Date lastModified ) {
164
+ if (lastModified == null ) {
165
+ return "-" ;
166
+ }
167
+ ZonedDateTime lastModifiedTime = ZonedDateTime .ofInstant (
168
+ lastModified .toInstant (),
169
+ ZoneId .systemDefault ()
170
+ );
171
+ DateTimeFormatter formatter = DateTimeFormatter .ofLocalizedDateTime (FormatStyle .MEDIUM )
172
+ .withLocale (Locale .getDefault ());
173
+
174
+ return formatter .format (lastModifiedTime );
175
+ }
176
+
177
+ private static void appendParentDirectory (Resource resource , StringBuilder builder , String relative , String parent ) {
178
+ builder .append ("<tr class='odd'><td><a class='icon up' href='" ).append (relative == null ? parent : relative + parent ).append (parent .endsWith ("/" ) ? "" : "/" ).append ("'>[..]</a></td><td>" );
179
+ builder .append (formatLastModified (resource .getLastModified ()))
180
+ .append ("</td><td>--</td></tr>\n " );
181
+ }
182
+
183
+ private static String getParentPath (String path , int state ) {
138
184
String parent = null ;
139
185
if (path .length () > 1 ) {
140
186
for (int i = path .length () - 1 ; i >= 0 ; i --) {
@@ -154,33 +200,89 @@ public static StringBuilder renderDirectoryListing(final HttpServerExchange exch
154
200
parent = "/" ;
155
201
}
156
202
}
203
+ return parent ;
204
+ }
157
205
158
- SimpleDateFormat format = new SimpleDateFormat ("MMM dd, yyyy HH:mm:ss" , Locale .US );
159
- int i = 0 ;
160
- if (parent != null ) {
161
- i ++;
162
- builder .append ("<tr class='odd'><td><a class='icon up' href='" ).append (relative == null ? parent : relative + parent ).append (parent .endsWith ("/" ) ? "" : "/" ).append ("'>[..]</a></td><td>" );
163
- builder .append (format .format ((resource .getLastModified () == null ? new Date (0L ) : resource .getLastModified ())))
206
+ private static void appendFiles (List <Resource > files , StringBuilder builder , int i , String sortUrl ) {
207
+ for (Resource entry : files ) {
208
+ builder .append ("<tr class='" ).append ((++i & 1 ) == 1 ? "odd" : "even" ).append ("'><td><a class='icon file' href='" )
209
+ .append (sortUrl ).append (entry .getName ()).append ("'>" )
210
+ .append (entry .getName ()).append ("</a></td><td>" )
211
+ .append (formatLastModified (entry .getLastModified ()))
212
+ .append ("</td><td>" );
213
+ formatSize (builder , entry .getContentLength ());
214
+ builder .append ("</td></tr>\n " );
215
+ }
216
+ }
217
+
218
+ private static void appendDirectories (List <Resource > directories , StringBuilder builder , int i , String sortUrl ) {
219
+ for (Resource entry : directories ) {
220
+ builder .append ("<tr class='" ).append ((++i & 1 ) == 1 ? "odd" : "even" ).append ("'><td><a class='icon dir' href='" )
221
+ .append (sortUrl ).append (entry .getName ()).append ("/'>" )
222
+ .append (entry .getName ()).append ("</a></td><td>" )
223
+ .append (formatLastModified (entry .getLastModified ()))
164
224
.append ("</td><td>--</td></tr>\n " );
165
225
}
226
+ }
166
227
228
+ private static Comparator <Resource > getComparator (String sortColumn , String currentSortOrder ) {
229
+ Comparator <Resource > comparator ;
230
+ if ("lastModified" .equals (sortColumn )) {
231
+ comparator = Comparator .comparing (
232
+ entry -> (entry .getLastModified () == null ) ? new Date (0L ) : entry .getLastModified ()
233
+ );
234
+ } else {
235
+ comparator = Comparator .comparing (Resource ::getName );
236
+ }
237
+
238
+ if ("desc" .equals (currentSortOrder )) {
239
+ comparator = comparator .reversed ();
240
+ }
241
+ return comparator ;
242
+ }
243
+
244
+ private static void separateDirectoriesAndFiles (Resource resource , List <Resource > directories , List <Resource > files ) {
167
245
for (Resource entry : resource .list ()) {
168
- builder .append ("<tr class='" ).append ((++i & 1 ) == 1 ? "odd" : "even" ).append ("'><td><a class='icon " );
169
- builder .append (entry .isDirectory () ? "dir" : "file" );
170
- builder .append ("' href='" ).append (relative == null ? path : relative + path ).append (entry .getName ()).append (entry .isDirectory () ? "/" : "" ).append ("'>" ).append (entry .getName ()).append ("</a></td><td>" );
171
- builder .append (format .format ((entry .getLastModified () == null ) ? new Date (0L ) : entry .getLastModified ()))
172
- .append ("</td><td>" );
173
246
if (entry .isDirectory ()) {
174
- builder . append ( "--" );
247
+ directories . add ( entry );
175
248
} else {
176
- formatSize ( builder , entry . getContentLength () );
249
+ files . add ( entry );
177
250
}
178
- builder .append ("</td></tr>\n " );
179
251
}
180
- builder . append ( "</tbody> \n </table> \n </body> \n </html>" );
252
+ }
181
253
254
+ private static StringBuilder buildDirectoryListingTable (String sortUrl , String sortColumn , String newSortOrder ) {
255
+ StringBuilder builder = new StringBuilder ();
256
+ builder .append ("<html>\n <head>\n <script src='" ).append (sortUrl ).append ("?js'></script>\n " )
257
+ .append ("<link rel='stylesheet' type='text/css' href='" ).append (sortUrl ).append ("?css' />\n </head>\n " );
258
+ builder .append ("<body onresize='growit()' onload='growit()'>\n <table id='thetable'>\n <thead>\n " );
259
+ builder .append ("<tr><th class='loc' colspan='3'>Directory Listing - " ).append (sortUrl ).append ("</th></tr>\n " )
260
+ .append ("<tr>" )
261
+ .append ("<th class='label offset'><a href='" ).append (sortUrl ).append ("?sort=name&order=" ).append ("name" .equals (sortColumn ) ? newSortOrder : "asc" ).append ("'>Name</a></th>" )
262
+ .append ("<th class='label'><a href='" ).append (sortUrl ).append ("?sort=lastModified&order=" ).append ("lastModified" .equals (sortColumn ) ? newSortOrder : "asc" ).append ("'>Last Modified</a></th>" )
263
+ .append ("<th class='label'>Size</th></tr>\n </thead>\n " );
264
+ builder .append ("<tfoot>\n <tr><th class=\" loc footer\" colspan=\" 3\" >Powered by Undertow</th></tr>\n </tfoot>\n <tbody>\n " );
182
265
return builder ;
266
+ }
183
267
268
+ private static String determineRelativePath (HttpServerExchange exchange , String path ) {
269
+ String relative = null ;
270
+ if (exchange != null ) {
271
+ final Map <String , Object > context = exchange .getAttachment (Predicate .PREDICATE_CONTEXT );
272
+ if (context != null ) {
273
+ final PathPrefixMatchRecord trans = (PathPrefixMatchRecord ) context
274
+ .get (PathPrefixPredicate .PREFIX_MATCH_RECORD );
275
+ if (trans != null ) {
276
+ if (trans .isOverWritten ()) {
277
+ relative = trans .getPrefix ();
278
+ if (!relative .endsWith ("/" ) && !path .startsWith ("/" )) {
279
+ relative += "/" ;
280
+ }
281
+ }
282
+ }
283
+ }
284
+ }
285
+ return relative ;
184
286
}
185
287
186
288
public static void renderDirectoryListing (HttpServerExchange exchange , Resource resource ) {
0 commit comments