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 .StringUtils ;
2628
2729import static java .nio .charset .StandardCharsets .*;
30+ import static java .time .format .DateTimeFormatter .*;
2831
2932/**
3033 * Represent the content disposition type and parameters as defined in RFC 2183.
@@ -45,16 +48,28 @@ public class ContentDisposition {
4548
4649 private final Long size ;
4750
51+ private final ZonedDateTime creationDate ;
52+
53+ private final ZonedDateTime modificationDate ;
54+
55+ private final ZonedDateTime readDate ;
56+
4857
4958 /**
5059 * Private constructor. See static factory methods in this class.
5160 */
52- private ContentDisposition (@ Nullable String type , @ Nullable String name , @ Nullable String filename , @ Nullable Charset charset , @ Nullable Long size ) {
61+ private ContentDisposition (@ Nullable String type , @ Nullable String name ,
62+ @ Nullable String filename , @ Nullable Charset charset , @ Nullable Long size ,
63+ @ Nullable ZonedDateTime creationDate , @ Nullable ZonedDateTime modificationDate , @ Nullable ZonedDateTime readDate ) {
64+
5365 this .type = type ;
5466 this .name = name ;
5567 this .filename = filename ;
5668 this .charset = charset ;
5769 this .size = size ;
70+ this .creationDate = creationDate ;
71+ this .modificationDate = modificationDate ;
72+ this .readDate = readDate ;
5873 }
5974
6075
@@ -100,6 +115,30 @@ public Long getSize() {
100115 return this .size ;
101116 }
102117
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+
103142
104143 /**
105144 * Return a builder for a {@code ContentDisposition}.
@@ -115,11 +154,12 @@ public static Builder builder(String type) {
115154 * Return an empty content disposition.
116155 */
117156 public static ContentDisposition empty () {
118- return new ContentDisposition (null , null , null , null , null );
157+ return new ContentDisposition (null , null , null , null , null , null , null , null );
119158 }
120159
121160 /**
122161 * Parse a {@literal Content-Disposition} header value as defined in RFC 2183.
162+ *
123163 * @param contentDisposition the {@literal Content-Disposition} header value
124164 * @return the parsed content disposition
125165 * @see #toString()
@@ -132,6 +172,9 @@ public static ContentDisposition parse(String contentDisposition) {
132172 String filename = null ;
133173 Charset charset = null ;
134174 Long size = null ;
175+ ZonedDateTime creationDate = null ;
176+ ZonedDateTime modificationDate = null ;
177+ ZonedDateTime readDate = null ;
135178 for (int i = 1 ; i < parts .length ; i ++) {
136179 String part = parts [i ];
137180 int eqIndex = part .indexOf ('=' );
@@ -155,12 +198,36 @@ else if (attribute.equals("filename") && (filename == null)) {
155198 else if (attribute .equals ("size" ) ) {
156199 size = Long .parseLong (value );
157200 }
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+ }
158225 }
159226 else {
160227 throw new IllegalArgumentException ("Invalid content disposition format" );
161228 }
162229 }
163- return new ContentDisposition (type , name , filename , charset , size );
230+ return new ContentDisposition (type , name , filename , charset , size , creationDate , modificationDate , readDate );
164231 }
165232
166233 /**
@@ -229,7 +296,16 @@ public boolean equals(Object o) {
229296 if (charset != null ? !charset .equals (that .charset ) : that .charset != null ) {
230297 return false ;
231298 }
232- return size != null ? size .equals (that .size ) : that .size == null ;
299+ if (size != null ? !size .equals (that .size ) : that .size != null ) {
300+ return false ;
301+ }
302+ if (creationDate != null ? !creationDate .equals (that .creationDate ) : that .creationDate != null ) {
303+ return false ;
304+ }
305+ if (modificationDate != null ? !modificationDate .equals (that .modificationDate ) : that .modificationDate != null ) {
306+ return false ;
307+ }
308+ return readDate != null ? readDate .equals (that .readDate ) : that .readDate == null ;
233309 }
234310
235311 @ Override
@@ -239,6 +315,9 @@ public int hashCode() {
239315 result = 31 * result + (filename != null ? filename .hashCode () : 0 );
240316 result = 31 * result + (charset != null ? charset .hashCode () : 0 );
241317 result = 31 * result + (size != null ? size .hashCode () : 0 );
318+ result = 31 * result + (creationDate != null ? creationDate .hashCode () : 0 );
319+ result = 31 * result + (modificationDate != null ? modificationDate .hashCode () : 0 );
320+ result = 31 * result + (readDate != null ? readDate .hashCode () : 0 );
242321 return result ;
243322 }
244323
@@ -267,6 +346,21 @@ public String toString() {
267346 builder .append ("; size=" );
268347 builder .append (this .size );
269348 }
349+ if (this .creationDate != null ) {
350+ builder .append ("; creation-date=\" " );
351+ builder .append (RFC_1123_DATE_TIME .format (this .creationDate ));
352+ builder .append ('\"' );
353+ }
354+ if (this .modificationDate != null ) {
355+ builder .append ("; modification-date=\" " );
356+ builder .append (RFC_1123_DATE_TIME .format (this .modificationDate ));
357+ builder .append ('\"' );
358+ }
359+ if (this .readDate != null ) {
360+ builder .append ("; read-date=\" " );
361+ builder .append (RFC_1123_DATE_TIME .format (this .readDate ));
362+ builder .append ('\"' );
363+ }
270364 return builder .toString ();
271365 }
272366
@@ -333,6 +427,21 @@ public interface Builder {
333427 */
334428 Builder size (Long size );
335429
430+ /**
431+ * Set the value of the {@literal creation-date} parameter.
432+ */
433+ Builder creationDate (ZonedDateTime creationDate );
434+
435+ /**
436+ * Set the value of the {@literal modification-date} parameter.
437+ */
438+ Builder modificationDate (ZonedDateTime modificationDate );
439+
440+ /**
441+ * Set the value of the {@literal read-date} parameter.
442+ */
443+ Builder readDate (ZonedDateTime readDate );
444+
336445 /**
337446 * Build the content disposition
338447 */
@@ -352,6 +461,13 @@ private static class BuilderImpl implements Builder {
352461
353462 private Long size ;
354463
464+ private ZonedDateTime creationDate ;
465+
466+ private ZonedDateTime modificationDate ;
467+
468+ private ZonedDateTime readDate ;
469+
470+
355471 public BuilderImpl (String type ) {
356472 Assert .hasText (type , "'type' must not be not empty" );
357473 this .type = type ;
@@ -382,9 +498,28 @@ public Builder size(Long size) {
382498 return this ;
383499 }
384500
501+ @ Override
502+ public Builder creationDate (ZonedDateTime creationDate ) {
503+ this .creationDate = creationDate ;
504+ return this ;
505+ }
506+
507+ @ Override
508+ public Builder modificationDate (ZonedDateTime modificationDate ) {
509+ this .modificationDate = modificationDate ;
510+ return this ;
511+ }
512+
513+ @ Override
514+ public Builder readDate (ZonedDateTime readDate ) {
515+ this .readDate = readDate ;
516+ return this ;
517+ }
518+
385519 @ Override
386520 public ContentDisposition build () {
387- return new ContentDisposition (this .type , this .name , this .filename , this .charset , this .size );
521+ return new ContentDisposition (this .type , this .name , this .filename , this .charset ,
522+ this .size , this .creationDate , this .modificationDate , this .readDate );
388523 }
389524 }
390525
0 commit comments