View Javadoc
1   /**
2    * Copyright (c) 2013-2023, jcabi.com
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met: 1) Redistributions of source code must retain the above
8    * copyright notice, this list of conditions and the following
9    * disclaimer. 2) Redistributions in binary form must reproduce the above
10   * copyright notice, this list of conditions and the following
11   * disclaimer in the documentation and/or other materials provided
12   * with the distribution. 3) Neither the name of the jcabi.com nor
13   * the names of its contributors may be used to endorse or promote
14   * products derived from this software without specific prior written
15   * permission.
16   *
17   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
19   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21   * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28   * OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package com.jcabi.github;
31  
32  import com.jcabi.aspects.Immutable;
33  import com.jcabi.aspects.Loggable;
34  import java.io.IOException;
35  import java.net.URL;
36  import java.text.ParseException;
37  import java.util.Date;
38  import javax.json.Json;
39  import javax.json.JsonObject;
40  import lombok.EqualsAndHashCode;
41  import lombok.ToString;
42  
43  /**
44   * Github Milestone.
45   *
46   * <p>Use a supplementary "smart" decorator to get other properties
47   * from an milestone, for example:
48   *
49   * <pre> Milestone.Smart milestone = new Milestone.Smart(origin);
50   * if (milestone.isOpen()) {
51   *   milestone.close();
52   * }
53   * </pre>
54   *
55   * @author Paul Polishchuk (ppol@ua.fm)
56   * @version $Id: a22479a075f06f662eb3d16fe08a41ff4cd05e84 $
57   * @see <a href="https://developer.github.com/v3/issues/milestones/">Milestones API</a>
58   * @since 0.7
59   */
60  @Immutable
61  @SuppressWarnings("PMD.TooManyMethods")
62  public interface Milestone extends Comparable<Milestone>,
63      JsonReadable, JsonPatchable {
64  
65      /**
66       * Milestone state.
67       */
68      String OPEN_STATE = "open";
69  
70      /**
71       * Milestone state.
72       */
73      String CLOSED_STATE = "closed";
74  
75      /**
76       * Repository we're in.
77       * @return Repo
78       */
79      Repo repo();
80  
81      /**
82       * Get its number.
83       * @return Milestone number
84       */
85      int number();
86  
87      /**
88       * Smart Milestone with extra features.
89       */
90      @Immutable
91      @ToString
92      @Loggable(Loggable.DEBUG)
93      @EqualsAndHashCode(of = { "milestone", "jsn" })
94      final class Smart implements Milestone {
95  
96          /**
97           * Name of mailestone state attribute.
98           */
99          private static final String STATE = "state";
100 
101         /**
102          * Name of mailestone description attribute.
103          */
104         private static final String DESCRIPTION = "description";
105 
106         /**
107          * Name of mailestone title attribute.
108          */
109         private static final String TITLE = "title";
110 
111         /**
112          * Name of mailestone due_on attribute.
113          */
114         private static final String DUE_ON = "due_on";
115 
116         /**
117          * Encapsulated milestone.
118          */
119         private final transient Milestone milestone;
120 
121         /**
122          * SmartJson object for convenient JSON parsing.
123          */
124         private final transient SmartJson jsn;
125 
126         /**
127          * Public ctor.
128          * @param mls Issue
129          */
130         public Smart(
131             final Milestone mls
132         ) {
133             this.milestone = mls;
134             this.jsn = new SmartJson(mls);
135         }
136 
137         /**
138          * Get its creator.
139          * @return Creator of milestone (who submitted it)
140          * @throws java.io.IOException If there is any I/O problem
141          */
142         public User creator() throws IOException {
143             return this.milestone.repo().github().users().get(
144                 this.jsn.value(
145                     "creator", JsonObject.class
146                 ).getString("login")
147             );
148         }
149 
150         /**
151          * Is it open?
152          * @return TRUE if it's open
153          * @throws IOException If there is any I/O problem
154          */
155         public boolean isOpen() throws IOException {
156             return Milestone.OPEN_STATE.equals(this.state());
157         }
158 
159         /**
160          * Open it (make sure it's open).
161          * @throws IOException If there is any I/O problem
162          */
163         public void open() throws IOException {
164             this.state(Milestone.OPEN_STATE);
165         }
166 
167         /**
168          * Close it (make sure it's closed).
169          * @throws IOException If there is any I/O problem
170          */
171         public void close() throws IOException {
172             this.state(Milestone.CLOSED_STATE);
173         }
174 
175         /**
176          * Get its state.
177          * @return State of milestone
178          * @throws IOException If there is any I/O problem
179          */
180         public String state() throws IOException {
181             return this.jsn.text(STATE);
182         }
183 
184         /**
185          * Change its state.
186          * @param state State of milestone
187          * @throws IOException If there is any I/O problem
188          */
189         public void state(
190             final String state
191         ) throws IOException {
192             this.milestone.patch(
193                 Json.createObjectBuilder().add(STATE, state).build()
194             );
195         }
196 
197         /**
198          * Get its title.
199          * @return Title of milestone
200          * @throws IOException If there is any I/O problem
201          */
202         public String title() throws IOException {
203             return this.jsn.text(TITLE);
204         }
205 
206         /**
207          * Change its title.
208          * @param title Title of milestone
209          * @throws IOException If there is any I/O problem
210          */
211         public void title(
212             final String title
213         ) throws IOException {
214             this.milestone.patch(
215                 Json.createObjectBuilder().add(TITLE, title).build()
216             );
217         }
218 
219         /**
220          * Get its description.
221          * @return Title of milestone
222          * @throws IOException If there is any I/O problem
223          */
224         public String description() throws IOException {
225             return this.jsn.text(DESCRIPTION);
226         }
227 
228         /**
229          * Change its description.
230          * @param description Description of milestone
231          * @throws IOException If there is any I/O problem
232          */
233         public void description(
234             final String description
235         ) throws IOException {
236             this.milestone.patch(
237                 Json.createObjectBuilder()
238                     .add(DESCRIPTION, description).build()
239             );
240         }
241 
242         /**
243          * Get its URL.
244          * @return URL of milestone
245          * @throws IOException If there is any I/O problem
246          */
247         public URL url() throws IOException {
248             return new URL(this.jsn.text("url"));
249         }
250 
251         /**
252          * When this milestone was created.
253          * @return Date of creation
254          * @throws IOException If there is any I/O problem
255          */
256         public Date createdAt() throws IOException {
257             try {
258                 return new Github.Time(
259                     this.jsn.text("created_at")
260                 ).date();
261             } catch (final ParseException ex) {
262                 throw new IllegalStateException(ex);
263             }
264         }
265 
266         /**
267          * The milestone due date.
268          * @return The milestone due date
269          * @throws IOException If there is any I/O problem
270          */
271         public Date dueOn() throws IOException {
272             try {
273                 return new Github.Time(
274                     this.jsn.text(DUE_ON)
275                 ).date();
276             } catch (final ParseException ex) {
277                 throw new IllegalStateException(ex);
278             }
279         }
280 
281         /**
282          * Change milestone due date.
283          * @param dueon New milestone due date
284          * @throws IOException If there is any I/O problem
285          */
286         public void dueOn(
287             final Date dueon
288         ) throws IOException {
289             this.milestone.patch(
290                 Json.createObjectBuilder()
291                     .add(DUE_ON, new Github.Time(dueon).toString()).build()
292             );
293         }
294 
295         /**
296          * Get number of open issues.
297          * @return Number of open issues
298          * @throws IOException If there is any I/O problem
299          */
300         public int openIssues() throws IOException {
301             return this.jsn.number("open_issues");
302         }
303 
304         /**
305          * Get number of closed issues.
306          * @return Number of closed issues
307          * @throws IOException If there is any I/O problem
308          */
309         public int closedIssues() throws IOException {
310             return this.jsn.number("closed_issues");
311         }
312 
313         @Override
314         public Repo repo() {
315             return this.milestone.repo();
316         }
317 
318         @Override
319         public int number() {
320             return this.milestone.number();
321         }
322 
323         @Override
324         public JsonObject json() throws IOException {
325             return this.milestone.json();
326         }
327 
328         @Override
329         public void patch(
330             final JsonObject json
331         ) throws IOException {
332             this.milestone.patch(json);
333         }
334 
335         @Override
336         public int compareTo(
337             final Milestone obj
338         ) {
339             return this.milestone.compareTo(obj);
340         }
341 
342     }
343 }