1919import java .io .ByteArrayOutputStream ;
2020import java .nio .charset .Charset ;
2121import java .nio .charset .StandardCharsets ;
22+ import java .time .ZonedDateTime ;
23+ import java .time .format .DateTimeParseException ;
2224
2325import org .springframework .lang .Nullable ;
2426import org .springframework .util .Assert ;
2527import org .springframework .util .ObjectUtils ;
2628import org .springframework .util .StringUtils ;
2729
2830import static java .nio .charset .StandardCharsets .*;
31+ import static java .time .format .DateTimeFormatter .*;
2932
3033/**
3134 * Represent the Content-Disposition type and parameters as defined in RFC 2183.
@@ -47,18 +50,26 @@ public class ContentDisposition {
4750
4851 private final Long size ;
4952
53+ private final ZonedDateTime creationDate ;
54+
55+ private final ZonedDateTime modificationDate ;
56+
57+ private final ZonedDateTime readDate ;
58+
5059
5160 /**
5261 * Private constructor. See static factory methods in this class.
5362 */
54- private ContentDisposition (@ Nullable String type , @ Nullable String name , @ Nullable String filename ,
55- @ Nullable Charset charset , @ Nullable Long size ) {
56-
63+ private ContentDisposition (@ Nullable String type , @ Nullable String name , @ Nullable String filename , @ Nullable Charset charset , @ Nullable Long size ,
64+ @ Nullable ZonedDateTime creationDate , @ Nullable ZonedDateTime modificationDate , @ Nullable ZonedDateTime readDate ) {
5765 this .type = type ;
5866 this .name = name ;
5967 this .filename = filename ;
6068 this .charset = charset ;
6169 this .size = size ;
70+ this .creationDate = creationDate ;
71+ this .modificationDate = modificationDate ;
72+ this .readDate = readDate ;
6273 }
6374
6475
@@ -104,6 +115,30 @@ public Long getSize() {
104115 return this .size ;
105116 }
106117
118+ /**
119+ * Return the value of the {@literal creation-date} parameter, or {@code null} if not defined.
120+ */
121+ @ Nullable
122+ public ZonedDateTime getCreationDate () {
123+ return this .creationDate ;
124+ }
125+
126+ /**
127+ * Return the value of the {@literal modification-date} parameter, or {@code null} if not defined.
128+ */
129+ @ Nullable
130+ public ZonedDateTime getModificationDate () {
131+ return this .modificationDate ;
132+ }
133+
134+ /**
135+ * Return the value of the {@literal read-date} parameter, or {@code null} if not defined.
136+ */
137+ @ Nullable
138+ public ZonedDateTime getReadDate () {
139+ return this .readDate ;
140+ }
141+
107142
108143 /**
109144 * Return a builder for a {@code ContentDisposition}.
@@ -119,11 +154,12 @@ public static Builder builder(String type) {
119154 * Return an empty content disposition.
120155 */
121156 public static ContentDisposition empty () {
122- return new ContentDisposition ("" , null , null , null , null );
157+ return new ContentDisposition ("" , null , null , null , null , null , null , null );
123158 }
124159
125160 /**
126161 * Parse a {@literal Content-Disposition} header value as defined in RFC 2183.
162+ *
127163 * @param contentDisposition the {@literal Content-Disposition} header value
128164 * @return the parsed content disposition
129165 * @see #toString()
@@ -136,6 +172,9 @@ public static ContentDisposition parse(String contentDisposition) {
136172 String filename = null ;
137173 Charset charset = null ;
138174 Long size = null ;
175+ ZonedDateTime creationDate = null ;
176+ ZonedDateTime modificationDate = null ;
177+ ZonedDateTime readDate = null ;
139178 for (int i = 1 ; i < parts .length ; i ++) {
140179 String part = parts [i ];
141180 int eqIndex = part .indexOf ('=' );
@@ -159,12 +198,36 @@ else if (attribute.equals("filename") && (filename == null)) {
159198 else if (attribute .equals ("size" ) ) {
160199 size = Long .parseLong (value );
161200 }
201+ else if (attribute .equals ("creation-date" )) {
202+ try {
203+ creationDate = ZonedDateTime .parse (value , RFC_1123_DATE_TIME );
204+ }
205+ catch (DateTimeParseException ex ) {
206+ // ignore
207+ }
208+ }
209+ else if (attribute .equals ("modification-date" )) {
210+ try {
211+ modificationDate = ZonedDateTime .parse (value , RFC_1123_DATE_TIME );
212+ }
213+ catch (DateTimeParseException ex ) {
214+ // ignore
215+ }
216+ }
217+ else if (attribute .equals ("read-date" )) {
218+ try {
219+ readDate = ZonedDateTime .parse (value , RFC_1123_DATE_TIME );
220+ }
221+ catch (DateTimeParseException ex ) {
222+ // ignore
223+ }
224+ }
162225 }
163226 else {
164227 throw new IllegalArgumentException ("Invalid content disposition format" );
165228 }
166229 }
167- return new ContentDisposition (type , name , filename , charset , size );
230+ return new ContentDisposition (type , name , filename , charset , size , creationDate , modificationDate , readDate );
168231 }
169232
170233 /**
@@ -225,7 +288,10 @@ public boolean equals(Object other) {
225288 ObjectUtils .nullSafeEquals (this .name , otherCd .name ) &&
226289 ObjectUtils .nullSafeEquals (this .filename , otherCd .filename ) &&
227290 ObjectUtils .nullSafeEquals (this .charset , otherCd .charset ) &&
228- ObjectUtils .nullSafeEquals (this .size , otherCd .size ));
291+ ObjectUtils .nullSafeEquals (this .size , otherCd .size ) &&
292+ ObjectUtils .nullSafeEquals (this .creationDate , otherCd .creationDate )&&
293+ ObjectUtils .nullSafeEquals (this .modificationDate , otherCd .modificationDate )&&
294+ ObjectUtils .nullSafeEquals (this .readDate , otherCd .readDate ));
229295 }
230296
231297 @ Override
@@ -235,6 +301,9 @@ public int hashCode() {
235301 result = 31 * result + ObjectUtils .nullSafeHashCode (this .filename );
236302 result = 31 * result + ObjectUtils .nullSafeHashCode (this .charset );
237303 result = 31 * result + ObjectUtils .nullSafeHashCode (this .size );
304+ result = 31 * result + (creationDate != null ? creationDate .hashCode () : 0 );
305+ result = 31 * result + (modificationDate != null ? modificationDate .hashCode () : 0 );
306+ result = 31 * result + (readDate != null ? readDate .hashCode () : 0 );
238307 return result ;
239308 }
240309
@@ -266,6 +335,21 @@ public String toString() {
266335 sb .append ("; size=" );
267336 sb .append (this .size );
268337 }
338+ if (this .creationDate != null ) {
339+ sb .append ("; creation-date=\" " );
340+ sb .append (RFC_1123_DATE_TIME .format (this .creationDate ));
341+ sb .append ('\"' );
342+ }
343+ if (this .modificationDate != null ) {
344+ sb .append ("; modification-date=\" " );
345+ sb .append (RFC_1123_DATE_TIME .format (this .modificationDate ));
346+ sb .append ('\"' );
347+ }
348+ if (this .readDate != null ) {
349+ sb .append ("; read-date=\" " );
350+ sb .append (RFC_1123_DATE_TIME .format (this .readDate ));
351+ sb .append ('\"' );
352+ }
269353 return sb .toString ();
270354 }
271355
@@ -332,6 +416,21 @@ public interface Builder {
332416 */
333417 Builder size (Long size );
334418
419+ /**
420+ * Set the value of the {@literal creation-date} parameter.
421+ */
422+ Builder creationDate (ZonedDateTime creationDate );
423+
424+ /**
425+ * Set the value of the {@literal modification-date} parameter.
426+ */
427+ Builder modificationDate (ZonedDateTime modificationDate );
428+
429+ /**
430+ * Set the value of the {@literal read-date} parameter.
431+ */
432+ Builder readDate (ZonedDateTime readDate );
433+
335434 /**
336435 * Build the content disposition
337436 */
@@ -351,6 +450,13 @@ private static class BuilderImpl implements Builder {
351450
352451 private Long size ;
353452
453+ private ZonedDateTime creationDate ;
454+
455+ private ZonedDateTime modificationDate ;
456+
457+ private ZonedDateTime readDate ;
458+
459+
354460 public BuilderImpl (String type ) {
355461 Assert .hasText (type , "'type' must not be not empty" );
356462 this .type = type ;
@@ -381,9 +487,28 @@ public Builder size(Long size) {
381487 return this ;
382488 }
383489
490+ @ Override
491+ public Builder creationDate (ZonedDateTime creationDate ) {
492+ this .creationDate = creationDate ;
493+ return this ;
494+ }
495+
496+ @ Override
497+ public Builder modificationDate (ZonedDateTime modificationDate ) {
498+ this .modificationDate = modificationDate ;
499+ return this ;
500+ }
501+
502+ @ Override
503+ public Builder readDate (ZonedDateTime readDate ) {
504+ this .readDate = readDate ;
505+ return this ;
506+ }
507+
384508 @ Override
385509 public ContentDisposition build () {
386- return new ContentDisposition (this .type , this .name , this .filename , this .charset , this .size );
510+ return new ContentDisposition (this .type , this .name , this .filename , this .charset ,
511+ this .size , this .creationDate , this .modificationDate , this .readDate );
387512 }
388513 }
389514
0 commit comments