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.util.Collection;
36  import java.util.Collections;
37  import java.util.LinkedList;
38  import lombok.EqualsAndHashCode;
39  import lombok.ToString;
40  
41  /**
42   * Github labels of an issue.
43   *
44   * @author Yegor Bugayenko (yegor256@gmail.com)
45   * @version $Id: 267043e4b6eca7b82a949bcccd7a88901bb788ee $
46   * @since 0.1
47   * @see <a href="https://developer.github.com/v3/issues/labels/">Labels API</a>
48   */
49  @Immutable
50  @SuppressWarnings("PMD.TooManyMethods")
51  public interface IssueLabels {
52  
53      /**
54       * The issue we're in.
55       * @return Issue
56       */
57      Issue issue();
58  
59      /**
60       * Add new labels.
61       * @param labels The labels to add
62       * @throws IOException If there is any I/O problem
63       * @see <a href="https://developer.github.com/v3/issues/labels/#add-labels-to-an-issue">Add labels to an issue</a>
64       */
65      void add(Iterable<String> labels) throws IOException;
66  
67      /**
68       * Replace all labels.
69       * @param labels The labels to save
70       * @throws IOException If there is any I/O problem
71       * @see <a href="https://developer.github.com/v3/issues/labels/#replace-all-labels-for-an-issue">Replace all labels for an issue</a>
72       */
73      void replace(Iterable<String> labels) throws IOException;
74  
75      /**
76       * Iterate them all.
77       * @return Iterator of labels
78       * @see <a href="https://developer.github.com/v3/issues/labels/#list-labels-on-an-issue">List Labels on an Issue</a>
79       */
80      Iterable<Label> iterate();
81  
82      /**
83       * Remove label by name.
84       * @param name Name of the label to remove
85       * @throws IOException If there is any I/O problem
86       * @see <a href="https://developer.github.com/v3/issues/labels/#remove-a-label-from-an-issue">Remove a Label from an Issue</a>
87       */
88      void remove(String name) throws IOException;
89  
90      /**
91       * Remove all labels.
92       * @throws IOException If there is any I/O problem
93       * @see <a href="https://developer.github.com/v3/issues/labels/#remove-all-labels-from-an-issue">Remove all labels from an issue</a>
94       */
95      void clear() throws IOException;
96  
97      /**
98       * Smart IssueLabels with extra features.
99       */
100     @Immutable
101     @ToString
102     @Loggable(Loggable.DEBUG)
103     @EqualsAndHashCode(of = "labels")
104     final class Smart implements IssueLabels {
105         /**
106          * Encapsulated labels.
107          */
108         private final transient IssueLabels labels;
109         /**
110          * Public ctor.
111          * @param lbl Labels
112          */
113         public Smart(final IssueLabels lbl) {
114             this.labels = lbl;
115         }
116         /**
117          * Label exists?
118          * @param name Name of the label
119          * @return TRUE if it exists
120          */
121         public boolean contains(final String name) {
122             boolean contains = false;
123             for (final Label label : this.labels.iterate()) {
124                 if (label.name().equals(name)) {
125                     contains = true;
126                     break;
127                 }
128             }
129             return contains;
130         }
131         /**
132          * Get label by name (runtime exception if absent).
133          * @param name Name of the label
134          * @return Label found (exception if not found)
135          * @since 0.7
136          */
137         public Label get(final String name) {
138             Label label = null;
139             int count = 0;
140             for (final Label opt : this.labels.iterate()) {
141                 if (opt.name().equals(name)) {
142                     label = opt;
143                     break;
144                 }
145                 ++count;
146             }
147             if (label == null) {
148                 throw new IllegalArgumentException(
149                     String.format(
150                         // @checkstyle LineLength (1 line)
151                         "label '%s' not found among %d others, use #contains() first",
152                         name, count
153                     )
154                 );
155             }
156             return label;
157         }
158         /**
159          * Add label if it is absent, don't touch its color if exists.
160          * @param name Name of the label
161          * @return TRUE if it was added
162          * @throws IOException If there is any I/O problem
163          */
164         public boolean addIfAbsent(final String name) throws IOException {
165             final boolean added;
166             if (this.contains(name)) {
167                 added = false;
168             } else {
169                 new Labels.Smart(this.labels.issue().repo().labels())
170                     .createOrGet(name);
171                 this.labels.add(Collections.singletonList(name));
172                 added = true;
173             }
174             return added;
175         }
176         /**
177          * Add label if it is absent, and set its color in any case.
178          * @param name Name of the label
179          * @param color Color to set
180          * @return TRUE if it was added
181          * @throws IOException If there is any I/O problem
182          * @since 0.7
183          */
184         public boolean addIfAbsent(
185             final String name, final String color
186         ) throws IOException {
187             Label label = null;
188             for (final Label opt : new Bulk<>(this.labels.iterate())) {
189                 if (opt.name().equals(name)) {
190                     label = opt;
191                     break;
192                 }
193             }
194             boolean added = false;
195             if (label == null) {
196                 added = true;
197                 label = new Labels.Smart(this.labels.issue().repo().labels())
198                     .createOrGet(name, color);
199                 this.labels.add(Collections.singletonList(name));
200             }
201             final Label.Smart smart = new Label.Smart(label);
202             if (!smart.color().equals(color)) {
203                 smart.color(color);
204             }
205             return added;
206         }
207         /**
208          * Select all labels with the given color.
209          * @param color Color
210          * @return Collection of labels with the provided color
211          * @throws IOException If there is any I/O problem
212          * @since 0.7
213          */
214         public Collection<Label> findByColor(final String color)
215             throws IOException {
216             final Collection<Label> found = new LinkedList<>();
217             for (final Label label : this.labels.iterate()) {
218                 if (new Label.Smart(label).color().equals(color)) {
219                     found.add(label);
220                 }
221             }
222             return found;
223         }
224         /**
225          * Remove label if it exists (do nothing otherwise).
226          * @param name Label to remove
227          * @return TRUE if it was removed, FALSE otherwise
228          * @throws IOException If there is any I/O problem
229          * @since 0.7
230          */
231         public boolean removeIfExists(final String name)
232             throws IOException {
233             boolean removed = false;
234             for (final Label label : this.labels.iterate()) {
235                 if (label.name().equals(name)) {
236                     this.remove(name);
237                     removed = true;
238                     break;
239                 }
240             }
241             return removed;
242         }
243         @Override
244         public Issue issue() {
245             return this.labels.issue();
246         }
247         @Override
248         public void add(final Iterable<String> names) throws IOException {
249             this.labels.add(names);
250         }
251         @Override
252         public void replace(final Iterable<String> names) throws IOException {
253             this.labels.replace(names);
254         }
255         @Override
256         public Iterable<Label> iterate() {
257             return this.labels.iterate();
258         }
259         @Override
260         public void remove(final String name) throws IOException {
261             this.labels.remove(name);
262         }
263         @Override
264         public void clear() throws IOException {
265             this.labels.clear();
266         }
267     }
268 }